Overview of Convolutional Neural Networks

  • Convolutional neural networks are designed to handle grid-structured data, such as image data, where there is a strong local dependency between the neighboring items of the grid.

  • Examples are images, text, and sound.

  • Here we only focus on image data.

  • For grid-structured data, convolutional neural networks are computationally more efficient than the fully connected neural networks. This is primarily because convolutional neural networks require fewer parameters than fully connected networks

  • Image data exhibits two key properties, namely, Translation invariance and Locality.

  • Translation invariance: The classification decision for each image is independent of the position of the animal on the image. A cat is a cat irrespective of whether it appears at the top or at the bottom of an image.
  • Locality: The classification decision does not really depend on a pixel that is far away from the animal on the image. A cat is still a cat irrespective of whether far away pixels correspond to a building or a tree.

  • Recall that when using the fully connected neural networks for images, the first step is to convert each image to an input features vector. By doing this we may lose both the above mentioned two structural properties.

Filtering

  • To understand convolutional neural networks it is helpful to have a basic understanding of filtering, a well-established field in signal processing.

  • filtering is a method or process that removes certain unwanted information from a signal or an image, or alternatively enhances it by accentuating certain information.

  • filtering applies mathematical operations on input images, with the most common operation being the convolution. A convolution can be viewed as an operation between two mathematical objects, such as two matrices, where one represents an image and the other a filter.

  • Each filter is often custom designed depending on the specific task at hand. For example, a popular filter, called the Sobel filter, is useful for the task of edge detection. See the following figure, where in (b) we detect the vertical edges and in (c) we detect the horizontal edges. By adding the outputs of these two filtering operations, we get the final image shown in (d) which captures most of the vertical and horizontal edge information.


Note: Convolutional neural networks build upon the classic ideas of filtering using convolutional layers. Each convolutional layer is made up of one or more filters, also known as kernels, each of which aims to extract a particular feature of the input to the layer.

Example CNN Network: VGG19

  • To get a feel for convolutional neural networks let us consider the task of classifying color images using the VGG19 model (VGG stands for Visual Geometry Group, the group at Oxford University that created the network).
  • This network is designed to train on images in ImageNet where the dimension of each image is \(3 \times 224 \times 224\), here \(3\) is the number of channels (red, green, and blue), and \(224\times 224\) specifies the pixel dimensions.
  • Since there are \(1,000\) classes in the ImageNet, the output of VGG19 is a probability vector of length \(1,000\).
  • The VGG19 model has about 144 million parameters

Practical 1:

  • Open Tutorial 1 of CNN available on the workshop GitHub page. Alternatively, click here.

  • Save a copy of this in your Google Colab.

  • In this exercise, we input two images, which are not part of the ImageNet dataset, to see what is the output of a pretrained VGG19.

  • Note that training of VGG19 takes many days on a regular computer, and hence we use a pretrained network.

The Convolution Operation

  • As mentioned earlier, the convolution operation is a key component of convolutional neural networks.

  • A convolution can be viewed as an operation on two functions which creates a third function. In finite domains, these functions may be represented as vectors, matrices, or tensors.

  • The convolution operation between two matrices \(W\) and \(x\) is denoted by \(W\star x\). Suppose \(K_h \times K_v\) and \(M_h \times M_v\) are the dimensions of \(W\) and \(x\), respectively.

  • In the context of CNNs, \(W\) represents a kernel at a layer and \(x\) represents an input to that layer. Further, it is usual that \(K_h \leq M_h\) and \(K_v \leq M_v\).

  • The convolution \(W\star x\) is a matrix of dimension \((M_h - K_h + 1) \times (M_v - K_v + 1),\) where for output at \(i' = 1, \dots, M_h - K_h + 1\) and \(j' = 1, \dots, M_v - K_v + 1\), the convolution action is,
\[\begin{equation} z_{i',j'} = (W \star x)_{i',\, j' } = \sum_{i = 0}^{K_h-1} \sum_{j = 0}^{K_v-1} w_{K_h -i , \, K_v - j} ~ x_{i' + i ,\, j' +j}, \end{equation}\]
  • This convolution operation is illustrated below where \(W\) is dimension \(3\times 3\) and \(x\) is dimension \(6\times 7\). Thus \(W\star x\) is dimension \((6-3+1)\times(7-3+1)\)

Convolution operation.


  • In particular, the first element of the output matrix \(z\) is computed as shown in the following figure.

Computing \(z_{1,1}\). Here, \(\odot\) denotes element-wise product between two matrices and \(\sum\) denotes summation of all the elements of a matrix.

Edge Detection

Recall the edge detection example presented earlier. We have seen that both horizontal and vertical edges are detected via convolution operation to obtain the final output.

  • The filter \(W\) for detecting the horizontal edges is given by
\[\begin{equation} \begin{bmatrix} -1 & -2 & -1\\ 0 & 0 & 0\\ 1 & 2 & 1\\ \end{bmatrix}. \end{equation}\]
  • The filter \(W\) for detecting the vertical edges is given by \[\begin{equation} \begin{bmatrix} -1 & 0 & 1\\ -2 & 0 & 2\\ -1 & 0 & 1\\ \end{bmatrix}. \end{equation}\]

Note 1:

  • The filtering operation at each layer in a CNN is similar to classical filtering, such as edge detection above. However, unlike classical filtering, filters in CNNs are learned rather than designed.

  • A training dataset is used for learning the filters before using the network for image processing. Here, learning a filter means learning the entries of the matrix that represents the filter, and these entries are called weights as the filters play a similar role to the weight matrices of a fully connected neural networks.


Note 2: Since filters in CNNs are learned rather than designed, there is no need of flipping operation, instead, we directly learn the flipped filter.

Building a Convolutional Layer

  • In the earlier part of this workshop, we have seen the construction of general fully connected neural networks, each of which consists of a series of layers where every neuron in a given layer is connected to every neuron in the next layer.

  • Fully connected networks are general in the sense that they are structure agnostic, that is, there are no specific assumptions made about the structure of the input. This property makes fully connected neural networks versatile.

  • However, fully connected networks are inadequate when dealing with the input that has rich structural properties such as images.

  • Convolutional neural networks make use of the aforementioned two key properties of grid-structured data, namely translation invariance and locality.

  • As a result, the number of parameters to learn in convolutional neural networks is significantly smaller than that of corresponding fully connected neural networks.

Can we use fully connected networks for image data?

Answer: Yes!

  • To use fully connected neural networks on images \(x\) of dimension \(M_h^{[0]} \times M_v^{[0]}\), they are first converted to a vector of length \(M_h^{[0]}\cdot M_v^{[0]}\) in a consistent manner.

  • Without loss of generality we can continue to index the elements of the vector \(x\) via tuples \((i, j) \in \{1, \dots, M_h^{[0]}\} \times \{1, \dots, M_v^{[0]}\}\).

  • The following figure shows matrix to vector conversion before inputting it to a fully connected layer.

matrix-to-vec

A consistent matrix-to-vector conversion operation.

 

  • The following figure shows reconstruction of the matrix from the corresponding vector. This operation can be used on outputs of a fully connected layer if the length of the output is a product of two integers.

vec-to-matrix

Reproducing matrix from the vector.

   

Issues:

    1. We lose both the structural properties: translation invariance and locality.
    1. Too many parameters to learn.

Motivating a Convolutional Layer (Simplified)

  1. Building a Layer with Local Understanding:
    In a fully connected layer, every neuron connects to every input, but images have spatial structure. In convolutional layers, we build a system that only looks at local regionsin the image, processing small areas at a time. This local focus helps us capture meaningful patterns, such as edges or textures, that are essential for understanding images.

  2. Reducing Parameters with Repeated Patterns:
    Convolutional layers reduce the number of parameters by applying the same filter across the image. This filter is like a small window that slides over the image, examining one small region at a time. Using a single filter repeatedly means we don’t need a new set of parameters for every region, making the network more efficient. It also gives the network a type of memory, helping it recognize patterns across the image.

  3. Translation Invariance:
    When we apply a filter across the entire image, the network gains translataion invariance. This means it can recognize patterns, such as the edge of an object, no matter where they appear in the image. For instance, if a network learns to detect edges or shapes, it doesn’t matter if those shapes are in the top left or bottom right—this invariance lets it recognize objects regardless of position. As an illustration, let us revisit edge detection and consider a pelican in flight as shown in following figures.

  • Figure (a):

Bird Bird

An input and the corresponding output of edge detection.



  • Figure (b):

Bird Bird

Another input and the corresponding output of edge detection.



  • Observe that Figures (a) and (b) are essentially the same, except for the fact that the position of the pelican in the output depends only on its position in the input. In other words, the filtering operation’s action on the object is generally independent of the location of the object in the image.
  1. Locality for Efficient Recognition:
    We now see further reduction of the parameters by invoking the second structural property, namely locality. Convolutional layers assume that nearby pixels are more relevant to each other than distant ones. In other word, a pixel \(x_{i,j}\) is not significantly influenced by far away pixels. A motivational illustration is in the following figures consisting of pelicans and seagulls, with each individual bird enclosed in a red box.


Bird Bird

Images of birds to illustrate the property of locality. The pixel information within each red box is sufficient to understand the characteristics of the bird inside the box.



  • Locality implies that if we are seeking information about a bird, then it is sufficient to know the pixel information only within the box that is enclosing the bird. Similarly, at a much finer level when we seek information about edges or similar features, it is often enough to consider 1, 2, or 3, neighboring pixels in each direction – yielding convolution kernels of size \(3\times 3\), \(5\times 5\), or \(7\times 7\) respectively. They’re designed to recognize details without being influenced by far-away pixels
  1. Building Complex Understanding from Simple Filters:
    By stacking multiple convolutional layers, each layer can capture increasingly complex features. Early layers might detect simple edges, while later ones recognize parts of objects or even entire objects.

General Operation of a Convolutional Layer

The operation of the layer can be represented as

\[\begin{equation} a^{[1]}=S^{[1]} (z^{[1]}) \ \ \ \textrm{where}\ \ \ \ z^{[1]} = (W\star x) + b \end{equation}\]
  • \(x\) is the input feature map.
  • \(W\) is the convolution kernel or filter
  • \(b\) is the scalar bias is element-wise to each element of the matrix term for layer \(W\star x\).
  • \(S^{[1]}(\cdot)\) an activation function (often a ReLU) is applied to the result

Summary: We have seen that at its core, a single convolutional layer involves the following actions on the input \(x\). First it is convolved with a convolution kernel \(W\). Then the result is shifted by a scalar bias \(b\). Finally an activation function \(S^{[1]}(\cdot)\) is applied. Note that when the input dimension is \(M_h^{[0]} \times M_v^{[0]}\), the dimension of the output is,

\[\begin{equation} M_h^{[1]} \times M_v^{[1]} = (M_h^{[0]} - K_h + 1) \times (M_v^{[0]} - K_v + 1). \end{equation}\]

 

Exercise: For an illustration of the reduction in the number of parameters that a convolutional layer has in comparison to a fully connected layer, consider an example with input dimension \(M_h^{[0]} \times M_v^{[0]} = 224 \times 224\) and a case with kernel dimension \(K_h \times K_v = 3\times 3\). Then the output dimension is \(M^{[1]}_h \times M^{[1]}_v = 222 \times 222\).

If we were to seek the same size of output dimension with a fully connected layer, we have \(222\times 222 = 49,284\) neurons. Since the input size is \(224\times 224 = 50,176\), the dimension of the weight matrix is the product of the input size and output size (number of neurons), and together with the bias vector (one entry for each neuron) we have \(2,472,923,268\) parameters.

In contrast, in the convolutional layer there are only \(3 \times 3 + 1 = 10\) parameters. While on its own, such a single convolution layer is certainly not as expressive as the fully connected layer with 2.5 billion learned parameters, as we see below, combining convolutional layers in tandem yields very powerful networks with much fewer parameters than their fully connected counterparts.

Alterations to the Convolution: Padding, Stride, and Dilation

  • The convolution appearing above is often tweaked and modified in the context of image data.

  • Specifically, alterations to the convolution operation, known as padding, stride, and dilation, are sometimes employed.

  • We now introduce and illustrate each of these alterations separately.

Padding

  • Recall the edge detection example above. Due to the convolution operation, the output image dimension is smaller than the input image dimension.

  • In particular, since the filter dimension \(K_h \times K_v\) is \(3 \times 3\) (Sobel filter), when the input dimension is \(M^{[0]}_h \times M^{[0]}_v\), the output dimension is equal to \((M^{[0]}_h -2) \times (M^{[0]}_v - 2)\).

  • Hence we see a slight reduction of the image size at the output. Since convolutional neural networks typically consist of several convolution layers, the dimension reductions in each of these layers can accumulate, making the overall downstream dimension undesirably small.

  • Padding is a simple solution to overcome this problem by adding extra zero-valued pixels around the input so that the effective input dimension is higher, and the desired output dimension is obtained.

  • In particular, padding is parameterized by a pair of non-negative integers \((p_h, p_v)\), where \(p_h\) and \(p_v\) respectively denote the number of all-zero rows and all-zero columns added to the input matrix. An example of padding is illustrated in the following figure.

Illustration of convolution with padding.



Output dimension after padding:

  • Again suppose that the input dimension is \(M^{[0]}_h\times M^{[0]}_v\) and the kernel dimension is \(K _h \times K _v\). Further, suppose each input image is modified by adding \(p_h\) rows roughly half on the top and half on the bottom, and \(p_v\) columns roughly half on the left and half on the right.

  • Then it is easy to check that the output dimension is

\[\begin{equation} \label{eq:dim-with-padding} M^{[1]}_h \times M^{[1]}_v = (M^{[0]}_h - K _h + p_h + 1) \times (M^{[0]}_v - K _v + p_v + 1). \end{equation}\]


Note:

  • The setting \((p_h, p_v) = (K_h-1, K_v-1)\) is a mechanism for ensuring that the input and the output are of the same dimension.

  • Also note that typically convolutional neural networks are designed to have kernels of odd height and odd width. Hence it is common to pad with exactly \(p_h/2\) rows of zeros on the left and \(p_h/2\) rows of zeros on the right, and similarly for the vertical dimension. This helps maintain spatial symmetry while conducting convolutions.

Stride

  • The convolutions we presented up to now involved shifts of the convolution kernel by one pixel at a time. This is called a convolution with a stride of one.

  • However, in many applications, we may wish to slide the convolution kernel with bigger steps in order to either reduce the computational cost, or to reduce the dimension of the output of the convolution layer.

  • In particular, we can conduct stride with positive integer valued parameters \((s_h, s_v)\), where \(s_h \geq 1\) and \(s_v \geq 1\) respectively denote the stride step sizes along height and width. An illustration is provided in the following figure.

Illustration of convolution with stride.


Output dimension after padding and stride:

With both padding of \((p_h, p_v)\) and stride of \((s_h, s_v)\), the output dimension will be

\[\begin{align} \label{eqn:Output_shape_after_stride} M^{[1]}_h \times M^{[1]}_v = \left(1 + \Big\lfloor \frac{M^{[0]}_h - K _h + p_h}{s_h} \Big\rfloor \right) \times \left(1 + \Big\lfloor \frac{M^{[0]}_v - K _v+ p_v}{s_v} \Big\rfloor\right). \end{align}\]

Dilation

  • Dilatation is a technique for increasing the receptive field of a filter without increasing the number of learned parameters.

  • This is achieved by spreading out the elements of the kernel matrix \(W\) via the insertion of zeros between elements. This alteration allows the kernel to cover a larger area of the input image, that is, the effective kernel dimension increases.

  • The level of dilation (or, the number of zeros added) is controlled using a pair of two positive integers \((d_h, d_v)\). In particular, it converts a kernel of size \(K_h \times K_v\) to a kernel of size \(K_h' \times K_v' = (d_h(K_h - 1) + 1) \times (d_v(K_v - 1) + 1)\) by adding all-zero columns and all-zero rows between the columns and rows of the original kernel.

  • See the following figure for illustration.

Figure (a): No dilation (i.e., \((d_h, d_v) = (1,1)\)). Figure (b): Dialtion with \((d_h, d_v) = (2,2)\). Source: He et al, 2021.



Figure: Illustration of dilation operation with \((d_h, d_v) = (2,2)\) extending a \(3\times 3\) filter to create a receptive field of \(5\times 5\) channels.



Output dimension after padding, stride and dilation:

  • The three alterations, namely padding with \((p_h, p_v)\), stride with \((s_h, s_v)\) and dilation with \((d_h, d_v)\), results in an output of dimension

\[\begin{equation} M_h^{[1]} \times M_v^{[1]} = \left(1 + \Big\lfloor \frac{M_h^{[0]} - d_h(K_h-1)-1 + p_h}{s_h} \Big\rfloor \right) \times \left(1 + \Big\lfloor \frac{M_v^{[0]} - d_v(K_v-1)-1 + p_v}{s_v} \Big\rfloor\right), \end{equation}\] \(\qquad\)where \(\lfloor u \rfloor\) represents the largest integer not greater than \(u\).

Inputs with Multiple Channels

  • So far in this section we have looked at the case where each input is a matrix, usually representing a grey scale image.

  • However, convolutional networks often deal with inputs comprised of multiple channels. For instance, a color image has three channels representing the red, green, and blue components.

  • When we have such data with multiple channels, input to a convolution layer is no longer a matrix but is rather represented as a three dimensional tensor of dimension, say, \(M^{[0]}_c \times M^{[0]}_h \times M^{[0]}_v\), where the depth \(M^{[0]}_c\) denotes the number of channels, and the other two numbers are for the horizontal and vertical dimensions as used previously.

  • Hence for color images we use \(M^{[0]}_c = 3\) and further, as we describe in the sequel, for a hidden layer we often have more than \(3\) input channels to the layer.

  • To deal with inputs with multiple channels, we use a kernel \(W\) with the same depth as the input. That is, we use a kernel of dimension \(K_c \times K_h \times K_v\) such that \(K_c = M^{[0]}_c\), \(K_h \leq M^{[0]}_h\), and \(K_v \leq M^{[0]}_v\).

Illustration of convolution when the input has multiple channels.



Outputs with Multiple Channels

  • Until now, regardless of the number of input channels, the output of a convolution layer is a matrix, denoted via \(a^{[1]}\). This is because, so far there is only one kernel, possibly a tensor, operating on the input to the convolution layer.

  • However, most popular convolutional neural networks have convolutional layers with multiple kernels operating on the input simultaneously. In this case, the output of the layer is a collection of matrices denoted by \(a^{[1,j]}\) for \(j=1, \ldots, M_c^{[1]}\), where \(M_c^{[1]}\) is the number of output channels. Consequently, the output can be viewed as a 3-dimensional tensor of dimension \(M^{[1]}_c \times M^{[1]}_h \times M^{[1]}_v\).

  • The following figure illustrate this.

Illustration of convolution when multiple kernels are used in parallel (i.e., multiple output channels).



Building a Convolutional Neural Network

  • We have now acquired all the crucial elements necessary for constructing convolutional neural networks, such as the VGG19 model depicted earlier. We now put the pieces together for constructing a convolutional neural network that, in addition to convolutional layers, includes fully connected layers, and pooling. layers.

  • A convolutional neural network is generally deep with multiple layers, similar to feed-forward networks. However, unlike feed-forward networks which consist of only fully connected layers, convolutional neural networks have different types of layers, of which some are trainable and the others are not, and the trainable layers are further broken up into convolutional layers and dense layers.

  • If we use \(L\) for the number of layers, then

\[ L = L_{\text{train}} + L_{\text{pool}}, \qquad \text{where} \qquad L_{\text{train}} = L_{\text{conv}} + L_{\text{dense}}. \]

  • Here \(L_{\text{train}}\), counts the number of trainable layers, whereas \(L_{\text{pool}}\) counts the number of layers that do not have trainable parameters. Further, the trainable layers are either convolutional layers, counted by \(L_{\text{conv}}\), or fully connected layers, counted by \(L_{\text{dense}}\).

  • For example, in the VGG19 network,

\[\begin{equation} \label{eq:counting-conv-layers} L = 24, \qquad L_{\text{train}} = 19, \qquad L_{\text{pool}} = 5, \qquad L_{\text{conv}} = 16, \qquad L_{\text{dense}} = 3, \end{equation}\]

  • See the following figure to verify these numbers.
  • Similar to a feed-forward network, the goal of a convolutional neural network is to approximate some unknown function \(f^*(\cdot)\). For instance, for classification of image data with animal faces, the function value \(f^*(x)\) for any given image \(x\) may yield a probability vector with the highest weight on the index associated with the label of the image \(x\).
  • A convolutional neural network defines a mapping \(f_{\mathbb{\theta}}(\cdot)\) and learns the values of the unknown parameters \(\mathbb{\theta}\) that ideally result in \(f^*(x) \approx f_\theta(x)\) for as many input images \(x\) as possible.
  • In general, similar to feed-forward networks, the approximating function \(f_\theta(\cdot)\) is recursively composed as

\[ f_{\mathbf{\theta}}(x)=f_{\mathbb{\theta}^{[L]}}^{[L]}(f_{\mathbb{\theta}^{[L-1]}}^{[L-1]}(\ldots (f_{\mathbb{\theta}^{[1]}}^{[1]}(x))\ldots)), \] \(\qquad\)where for each \(\ell\), the function \(f_{{\mathbb{\theta}}^{[\ell]}}^{(\ell)}(\cdot)\) is associated with the \(\ell\)th layer which depends on the layer’s parameters \({\mathbb{\theta}}^{[\ell]} \in \Theta^{[\ell]}\).

  • Note that for layers that are not trainable (as counted via \(L_{\text{pool}}\)), the parameter space \(\Theta^{[\ell]}\) is empty.
  • Similarly to feed-forward networks, it is useful to denote the neuron activations of the network via \(a^{[1]},a^{[2]} \ldots, a^{[L]}\) where \(a^{[L]} = \hat{y}\) is the output, and for \(\ell=1,\ldots,L-1\), \[ a^{[\ell]} = f^{[\ell]}_{\theta^{[\ell]}}(a^{[\ell-1]}), \] with \(a^{[0]} = x\).

Convolutional Layers

  • When the \(\ell\)-th layer of the network is a convolution layer, then \(f_{{\mathbb{\theta}}^{[\ell]}}^{(\ell)}(\cdot)\) take the output \(a^{[\ell-1]}\) of the previous layer as the input and conducts convolution.

  • In this case the input and output are generally 3-dimensional tensors as we have seen in the previously.

Pooling Layers

  • As mentioned above, there are also non-trainable layers counted by \(L_{\text{pool}}\) and these are typically called pooling layers. The main idea of a pooling layer is to reduce the height and width of the input tensor \(a^{[\ell-1]}\) to achieve a lower dimensional output tensor \(a^{[\ell]}\) without changing the depth.

  • Generally for some fixed channel \(j\), and pixel coordinates of the output \((i,k)\), a pooling operation operates on pixels from a window in the input denoted via \(\mathscr{I}_{(i,k)}\). Here \(\mathscr{I}_{(i,k)}\) is a set of pixel coordinates in the input that are maped to the specific output pixel \((i,k)\). There are two popular pooling techniques used in practice, namely, max-pooling and average-pooling.

  • For each channel \(j\), the pooling operation can be summarized as,

\[ \Big[a^{[\ell]}_{(j)}\Big]_{i,k} = \begin{cases} \max_{(i',k') \in \mathscr{I}_{(i,k)}} ~\Big[a^{[\ell-1]}_{(j)} \Big]_{i',k'}, & \qquad \text{(max-pooling)} \\[15pt] \frac{1}{|\mathscr{I}_{(i,k)}|} \sum_{(i',k') \in \mathscr{I}_{(i,k)}} ~\Big[a^{[\ell-1]}_{(j)} \Big]_{i',k'}. & \qquad \text{(average-pooling)} \end{cases} \]

  • As is evident, max-pooling takes the maximal pixel value within the window as the output, while average pooling averages pixel values within the window for the output. See the following figure for an illustration.



Note: The idea of pooling interplays with the notion that the initial layers of a convolutional network focus on pixel level features similar to edge detection, and as we progress towards the final layers of the network, the information is aggregated to address general questions about the whole image. Thus deeper layers are less sensitive to translation changes on the input image compared to the initial layers. For instance, the answer to a question ``is there a bird in the photo?’’ is the same irrespective of it is pelican or a seagull, even though the corresponding outputs from the initial layers look different. Pooling layers are applied after convolutional layers to support this aggregation by progressively reducing the spatial dimensions, allowing the network to recognize larger patterns while focusing less on specific pixel locations.

Fully Connected Layers

  • Some layers of a CNN can be fully connected. Such layers are typically deployed at the end of the network. This is because the typical task of the last layers of convolutional neural network is to address general questions, such as classification of the objects in the image.

  • Note that since fully connected layers operate on vectors as the input, in cases the previous layer has a tensor as output, it is flattened to a vector.

Practice Excercise

Practical 2:

  • Open Tutorial 2 of CNN available on the workshop GitHub page. Alternatively, click here.

  • Save a copy of this in your Google Colab.

  • In this exercise, we build a simple CNN for classification using CiFAR10 dataset.

VGG19 Revisited

  • We now take a closer look at the architecture of our running example network, VGG19.

  • While this is not the most modern convolutional architecture, it is instructive to consider it here since it falls directly within the paradigms discussed above.

  • Table below provides complete details.



  • The total number of learned parameters of VGG19 networks is around 138 millions.

Dropout

  • Dropout is another popular technique for improving the performance of CNNs. One simple tip for using dropout is to include it after each fully connected layer in the network. This helps to regularize the network and prevent overfitting, which can improve the generalization performance on new data.

  • Dropout is typically not used after convolutional layers in CNNs, but rather after fully connected layers. In convolutional layers, the neurons are typically spatially arranged in a grid, and dropping out individual neurons could disrupt the spatial structure of the feature maps. However, some researchers have explored alternative forms of spatial dropout that are designed specifically for convolutional layers, so it’s an area of ongoing research.

Note: It’s also important to ctune the hyperparameter of dropout, which is the dropout probability, to find the optimal value for your specific network and dataset. Additionally, dropout can sometimes be sensitive to the network architecture and the order in which layers are connected, so it’s worth experimenting with different architectures and layer connections to see if performance improves. Finally, it’s important to note that dropout can sometimes be omitted in small CNNs or shallow networks, as the benefits may not be significant compared to the added computational overhead.

Batch Normalization

  • Similar to feed-forward networks, batch normalization is a popular technique for improving the performance of CNNs.

  • One simple tip for using batch normalization is to include it after each convolutional or fully connected layer in the network. This helps to normalize the output of each layer and reduce the internal covariate shift, and this can improve the overall stability and speed of training.

Note: Batch normalization can sometimes be sensitive to the batch size used during training, so it’s worth experimenting with different batch sizes to see if performance improves. It is important to note that batch normalization can sometimes be omitted in small CNNs or shallow networks, as the benefits may not be significant compared to the added computational overhead.

Understanding Inner Layers and Derived Features

  • Visualizing and Understanding Convolutional Networks” is a paper by Matthew Zeiler and Rob Fergus, which proposes a method for visualizing and understanding the internal representations learned by CNNs.

  • The authors start by highlighting the importance of understanding how CNNs work. Specifically, they focus on the visualization of the activation patterns in the feature maps produced by each layer of the network. The following figure provide some understanding of the features between the layers. For more details refer to the original paper.

  • The network has many channels across multiple layers, and here we present only a few of those channels, focusing on a pair of arbitrary channels within each of the layers \(2\), \(3\), \(4\), and \(5\).

  • Each channel that we visualize has a \(3 \times 3\) grid of synthesized images (channel visualization) as well as a matching \(3 \times 3\) grid of parts of images from a dataset (original receptive field). These channel visualizations and original receptive fields can serve as a visual interpretation of what the specific channel detects.

  • For example, we see that the two channels visualized in layer~\(2\) detect simple features with one channel focusing on edges and another channel focusing on circles.

  • As we advance deeper in the network we see that the type of visual patterns detected are much more complex. For example, the two channels presented for layer~\(4\) detect parts of animals, and the channels of layer~\(5\) detect such representations as well.

  • Note however, that one of the channels in layer~\(5\) that we present appears to detect either faces or car wheels even though these are very different objects. Hence any attempt to categorize channels based on their ``meaning’’ alone is far from absolute.

  • Nevertheless, a visual representation such as this Figure can help to understand the function of individual channels within the network.

  • In summary, early layers of the network tend to detect simple features such as edges and textures, while later layers detect more complex features such as object parts and entire objects.

Other Landmark Architectures

In addition to VGG19 architecture that achieved high accuracy in the ImageNet Large Scale Visual Recognition Challenge (ILSVRC) 2014, there are few other landmark architectures. The following list is small summary of these models.

  • ResNet (Residual Network): ResNet is a CNN architecture that introduced residual blocks, which allow for the reuse of feature maps from earlier layers by adding skip connections. This enables very deep networks (e.g. 152 layers) to be trained and achieve state-of-the-art performance on image classification tasks.
A shortcut connection (residual connection) as part of a residual network.
  • Inception (GoogLeNet): Inception is a CNN architecture that introduced the Inception module, which uses multiple filter sizes in parallel to extract features at different scales. This allows for efficient use of computational resources and achieves high accuracy on image classification tasks.
One form of an inception module, playing part in an inception network. The key idea is parallel computation of various paths followed by a concatenating of the outputs from all paths.
  • The Inception network is a type of convolutional neural network architecture that was introduced in 2014 by Google researchers.

  • The main idea behind the Inception network is to use multiple filter sizes in parallel at each layer of the network, in order to capture features of different scales and resolutions.

  • This approach is different from other popular CNN architectures, such as VGG, which typically use small (3x3) filters in all of their convolutional layers.

  • The Inception network also includes the use of so-called bottleneck layers, which use 1x1 convolutions to reduce the dimensionality of the input before applying larger filters. This helps to reduce the number of parameters in the network, while still allowing it to capture complex features.

  • One of the key innovations of the Inception network is its use of an Inception module, which is a basic building block of the network that combines the various filter sizes and bottleneck layers. The Inception module allows the network to capture complex and multi-scale features at each layer, while also reducing the number of parameters in the network.

  • MobileNet: MobileNet is a CNN architecture designed for mobile and embedded devices that uses depthwise separable convolutions to reduce the number of parameters and computations required while maintaining high accuracy. It achieves state-of-the-art performance on mobile platforms and real-time video analysis.
  • DenseNet: DenseNet is a CNN architecture that introduces dense connectivity between layers, where each layer receives feature maps from all preceding layers. This promotes feature reuse and reduces the number of parameters required, leading to high accuracy and efficient training.
  • AlexNet: AlexNet is a CNN architecture that was the winner of the ILSVRC 2012. At the time, it was a breakthrough in image classification, achieving state-of-the-art performance on the ImageNet datase. It introduced the concept of using ReLU activation functions, dropout regularization, and data augmentation for training deep neural networks. It also used GPU acceleration to speed up training and achieved a significant reduction in error rate on image classification tasks.
  • EfficientNet is a family of convolutional neural network architectures that were developed with the aim of providing better accuracy and efficiency in terms of model size and computation cost. The key idea is to systematically scale up the dimensions of the network’s parameters (such as depth, width, and resolution) in a balanced way, while also introducing a new compound scaling method that optimizes these dimensions based on a set of pre-defined constraints.
Performance of various convolutional models as well as efficient net

Beyond Classification

  • far we have focused on the internals of convolutional neural networks with a particular emphasis on the task of image classification, e.g. determining if an image is that of a cat or a dog.

  • However, there are several other important image analysis tasks that are also handled with convolutional neural networks. These tasks deal with analysis and understanding of an image including the location of objects, the count of objects, separating between different semantic features of the image, and more.

Note: In terms of the input data, it is important to keep in mind that not all data is made of monochrome or color images. Within computer vision, one often deals with image sequences (short movies), or images that have more than 3 channels. For example, some images may also have a distance channel capturing the distance from the camera per pixel. Further, non-image data can also be handled via convolutional networks. One such example is fMRI (functional magnetic resonance imaging) data which is 4 dimensional in nature as it records the state of physical locations (e.g., blood-oxygen-level-dependent (BOLD) signal) in three dimensions over time.

Convolutional Networks and Key Computer Vision Tasks

  • As mentioned above, classification serves as a simple and useful example. For an input image \(x\), a convolutional neural network \(f_\theta(\cdot)\), has output \(\hat{y} = f_\theta(x)\) which is a vector of probabilities where the highest probability typically determines the appropriate label for the image.

  • As was evident from our detailed study of the VGG19 model and other architectures, initial layers of the model \(f_\theta(\cdot)\) are typically convolutional, and the final layers are typically fully connected layers.

  • These final layers help transform the internal derived features in the network into the output vector of probabilities \(\hat{y}\).

  • When one considers tasks other than classification, it is often common to replace the final layers of the network with other layers such that the output \(\hat{y}\) suites the desired task.

  • The following figure illustrates key computer vision tasks for images.


  • Object localization which is the task of identifying the location of an object in an image, as well as possibly the type of object in which case the task is called localization and classification.

  • Object detection which is the task of detecting multiple instances of an object in an image, also separating between the objects and classifying their type.

  • Landmark detection, which is the task of identifying the specific pixel locations of landmarks in an image.

  • Semantic segmentation, which is the process of classifying each individual pixel to be of a different class from a finite set of classes (pixel wise classification).

  • Instance segmentation, which finds different instances of objects in the image and separates pixels to be of different instances.

  • Identification (face recognition), which determines if an image is that of a specific instance (or person).


Let us now consider possible forms of the output \(\widehat{y}\)

  • For object localization, \(\widehat{y}\) needs to contain information about a bounding box which locates the object. This can be in the form of \((\widehat{y}_x,\widehat{y}_y,\widehat{y}_h,\widehat{y}_w)\) where \(\widehat{y}_x\) and \(\widehat{y}_y\) are the coordinates of (say) the upper left corner of the bounding box and \(\widehat{y}_h\), \(\widehat{y}_w\) are the height and width of the bounding box, respectively.

  • For object detection, a collection of multiple bounding boxes needs to be supplied.

  • For landmark detection, a list of coordinates of the locations of landmarks comprises the output.

  • For semantic segmentation, each pixel location in the input image, \(x\), has an associated probability vector of classes in the output \(\widehat{y}\). Hence in this case, \(\widehat{y}\) can be represented as a tensor with width and height dimensions the same as the input image, and a depth dimension which is the number of classes in the segmentation.

  • For instance segmentation, the output is similar to that of semantic segmentation, but instead of recording probabilities of classes, the depth dimension of the output \(\widehat{y}\) is used for determining the specific instance of any given pixel.

  • Finally, in the case of identification, or face recognition, the output is often just a probability as in a binary classifier, since the task is to determine if a face image matches a given pre-stored template or not. The speciality in this case is that the input \(x\) is typically composed of two images, where one is the template of the person (e.g. a stored image in a security database), and the other image is the current image taken.


Object Localization

  • To get a feel for object localization assume that we wish to train a CNN that operates on an input image \(x\) and determines if the image contains a \(\texttt{bird}\) or a \(\texttt{plane}\) (classification).

  • The model’s second goal is to determine the specific location \((\widehat{y}_x,\widehat{y}_y,\widehat{y}_h,\widehat{y}_w)\) of that object (localization). Images with multiple birds or planes are not considered. Images without a bird and without a plane are possible and in this case the output yields \(\texttt{nothing}\).

One way to encode the output is \[ \widehat{y}=(\widehat{p}_{\texttt{nothing}}, ~\widehat{p}_{\texttt{bird}}, ~\widehat{p}_{\texttt{plane}}, \widehat{y}_x,\widehat{y}_y,\widehat{y}_h,\widehat{y}_w), \]

where as in standard classification examples \((\widehat{p}_{\texttt{nothing}}, ~\widehat{p}_{\texttt{bird}}, ~\widehat{p}_{\texttt{plane}})\) is a probability vector, and the other coordinates define a bounding box.

  • Here an output that has \(\widehat{p}_{\texttt{nothing}}\) greater than each of \(\widehat{p}_{\texttt{bird}}\) and \(\widehat{p}_{\texttt{plane}}\) implies a prediction of no bird and no plane.

  • In terms of training data, for each input image we denote the output as \(y\) where images without a bird or a plane are labeled as, \(y = (1, 0, 0, \emptyset,\emptyset,\emptyset,\emptyset)\), where \(\emptyset\) are ``do not care’’ values. Images with a bird are labeled as \(y = (0,1,0, {y}_x, {y}_y, {y}_h,{y}_w)\) where the bounding box \(({y}_x, {y}_y, {y}_h,{y}_w)\) is typically based on a manual determination by a human annotator. Similarly, images with a plane are labeled as \(y = (0,0,1, {y}_x, {y}_y, {y}_h,{y}_w)\).

  • We now construct a loss function that captures closeness of \(\widehat{y}\) and \(y\). For this we first separate the classification and localization objectives into a loss \(C_{\text{classification}}(\theta \,;\, \widehat{y}, y)\) and \(C_{\text{localization}}(\theta \,;\, \widehat{y}, y)\). The former depends only on the probability components in \(\widehat{y}\) and \(y\), and the latter depends only on the bounding box components in \(\widehat{y}\) and \(y\). For the classification loss, we use categorical cross entropy. For the localization loss, we use a mean squared error, applied to the four bounding box components.

The two separate losses are then combined such that the loss for a specific observation is,

\[ C_{\text{classification}}(\theta \,;\, \widehat{y}, y) ~+~ \gamma \cdot (1-y_1) \cdot C_{\text{localization}}(\theta \,;\, \widehat{y}, y), \]

where \(\gamma > 0\) is a hyper-parameter used to weigh the two losses and taken as \(\gamma=1\) by default. Observe that \(y_1 = 1\) when the label is \(\texttt{nothing}\) and is otherwise \(0\) and thus for labels in the training data without a bird or a plane only the classification objective is used.

  • To perform object localization, say with a model like VGG19, the network can be modified by adding additional layers at the end of the architecture to predict the coordinates of the bounding box. This can be achieved by attaching a regression head to the output of the final convolutional layer of the network. The regression head consists of fully connected layers that predict the coordinates of the bounding box. Such simple modifications of networks that were otherwise designed for classification are always possible.

Practice Excercise

Practical 3:

  • Open Tutorial 3 of CNN available on the workshop GitHub page. Alternatively, click here.

  • Save a copy of this in your Google Colab.

  • In this exercise, we build a simple CNN for classification using MNIST dataset.

LS0tCnRpdGxlOiAiQSB3b3Jrc2hvcCBvbjogTUFUSEVNQVRJQ0FMIEVOR0lORUVSSU5HIE9GIERFRVAgTEVBUk5JTkcgLSBGT1VOREFUSU9OcyIKYXV0aG9yOgogIG5hbWU6IFByZXNlbnRlZCBieSBCZW5vaXQgTGlxdWV0CiAgYWZmaWxpYXRpb246IFVuaXZlcnNpdHkgT2YgUGF1IGV0IFBheXMgZGUgTCdBZG91cgogIGhlYWRlci1pbmNsdWRlczogXHVzZXBhY2thZ2V7YW1zbWF0aH0sXHVzZXBhY2thZ2V7eGNvbG9yfQpzdWJ0aXRsZTogIiBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrcyIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB5ZXMKICAgIHRoZW1lOiBjZXJ1bGVhbgogICAgaGlnaGxpZ2h0OiBrYXRlCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KCmBgYHtyLGVjaG89RkFMU0V9CmNvbG9yaXplIDwtIGZ1bmN0aW9uKHgsIGNvbG9yKSB7CiAgaWYgKGtuaXRyOjppc19sYXRleF9vdXRwdXQoKSkgewogICAgc3ByaW50ZigiXFx0ZXh0Y29sb3J7JXN9eyVzfSIsIGNvbG9yLCB4KQogIH0gZWxzZSBpZiAoa25pdHI6OmlzX2h0bWxfb3V0cHV0KCkpIHsKICAgIHNwcmludGYoIjxzcGFuIHN0eWxlPSdjb2xvcjogJXM7Jz4lczwvc3Bhbj4iLCBjb2xvciwKICAgICAgeCkKICB9IGVsc2UgeAp9CmBgYAoKCgotIEJhc2VkIG9uICBDaGFwdGVyICoqNioqIG9mIHRoZSBib29rIFtNYXRoZW1hdGljYWwgRW5naW5lZXJpbmcgb2YgRGVlcCBMZWFybmluZ10oaHR0cHM6Ly9kZWVwbGVhcm5pbmdtYXRoLm9yZy8pIGJ5IEJlbm9pdCBMaXF1ZXQsIFNhcmF0IE1va2EgYW5kIFlvbmkgTmF6YXJhdGh5IAoKLSAgIERhdGEgYXJlIGF2YWlsYWJsZSBhdCA8aHR0cHM6Ly9naXRodWIuY29tL2Jlbm9pdC1saXF1ZXQvSUFTQy1ETD4KCgoKIyBPdmVydmlldyBvZiBDb252b2x1dGlvbmFsIE5ldXJhbCBOZXR3b3JrcwoKLSAgICpDb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3JrcyogYXJlIGRlc2lnbmVkIHRvIGhhbmRsZSAqZ3JpZC1zdHJ1Y3R1cmVkIGRhdGEqLCBzdWNoIGFzIGltYWdlIGRhdGEsIHdoZXJlIHRoZXJlIGlzIGEgc3Ryb25nIGxvY2FsIGRlcGVuZGVuY3kgYmV0d2VlbiB0aGUgbmVpZ2hib3JpbmcgaXRlbXMgb2YgdGhlIGdyaWQuCgotICAgRXhhbXBsZXMgYXJlIGByIGNvbG9yaXplKCJpbWFnZXMiLCJibHVlIilgLCB0ZXh0LCBhbmQgc291bmQuCgotICAgSGVyZSB3ZSBvbmx5IGZvY3VzIG9uIGByIGNvbG9yaXplKCJpbWFnZSIsImJsdWUiKWAgZGF0YS4KCi0gICBGb3IgZ3JpZC1zdHJ1Y3R1cmVkIGRhdGEsIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzIGFyZSBjb21wdXRhdGlvbmFsbHkgbW9yZSBlZmZpY2llbnQgdGhhbiB0aGUgZnVsbHkgY29ubmVjdGVkIG5ldXJhbCBuZXR3b3Jrcy4gVGhpcyBpcyBwcmltYXJpbHkgYmVjYXVzZSBgciBjb2xvcml6ZSgiY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MiLCJyZWQiKWAgcmVxdWlyZSBgciBjb2xvcml6ZSgiZmV3ZXIiLCJibHVlIilgIHBhcmFtZXRlcnMgdGhhbiBmdWxseSBjb25uZWN0ZWQgbmV0d29ya3MKCi0gICBJbWFnZSBkYXRhIGV4aGliaXRzIHR3byBrZXkgcHJvcGVydGllcywgbmFtZWx5LCBgciBjb2xvcml6ZSgiVHJhbnNsYXRpb24gaW52YXJpYW5jZSIsInJlZCIpYCBhbmQgYHIgY29sb3JpemUoIkxvY2FsaXR5IiwicmVkIilgLgoKOjo6IGJsdWUKLSAgICoqVHJhbnNsYXRpb24gaW52YXJpYW5jZSoqOiBUaGUgY2xhc3NpZmljYXRpb24gZGVjaXNpb24gZm9yIGVhY2ggaW1hZ2UgaXMgXyoqaW5kZXBlbmRlbnQqKl8gb2YgdGhlIHBvc2l0aW9uIG9mIHRoZSBhbmltYWwgb24gdGhlIGltYWdlLiBBIGNhdCBpcyBhIGNhdCBpcnJlc3BlY3RpdmUgb2Ygd2hldGhlciBpdCBhcHBlYXJzIGF0IHRoZSB0b3Agb3IgYXQgdGhlIGJvdHRvbSBvZiBhbiBpbWFnZS4KOjo6CgoKCjo6OiBvcmFuZ2UKLSAgICoqTG9jYWxpdHkqKjogVGhlIGNsYXNzaWZpY2F0aW9uIGRlY2lzaW9uIGRvZXMgbm90IHJlYWxseSBkZXBlbmQgb24gYSBwaXhlbCB0aGF0IGlzIF8qKmZhciBhd2F5KipfIGZyb20gdGhlIGFuaW1hbCBvbiB0aGUgaW1hZ2UuIEEgY2F0IGlzIHN0aWxsIGEgY2F0IGlycmVzcGVjdGl2ZSBvZiB3aGV0aGVyIGZhciBhd2F5IHBpeGVscyBjb3JyZXNwb25kIHRvIGEgYnVpbGRpbmcgb3IgYSB0cmVlLgo6OjoKCl9fXwoKLSAgIFJlY2FsbCB0aGF0IHdoZW4gdXNpbmcgdGhlIGZ1bGx5IGNvbm5lY3RlZCBuZXVyYWwgbmV0d29ya3MgZm9yIGltYWdlcywgdGhlIGZpcnN0IHN0ZXAgaXMgdG8gKipjb252ZXJ0IGVhY2ggaW1hZ2UgdG8gYW4gaW5wdXQgZmVhdHVyZXMgdmVjdG9yKiouIEJ5IGRvaW5nIHRoaXMgd2UgbWF5IGByIGNvbG9yaXplKCJsb3NlIiwicmVkIilgIGJvdGggdGhlIGFib3ZlIG1lbnRpb25lZCB0d28gc3RydWN0dXJhbCBwcm9wZXJ0aWVzLgoKX19fCgojIyMgRmlsdGVyaW5nCgotICAgVG8gdW5kZXJzdGFuZCBjb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3JrcyBpdCBpcyBoZWxwZnVsIHRvIGhhdmUgYSBiYXNpYyB1bmRlcnN0YW5kaW5nIG9mIGByIGNvbG9yaXplKCJmaWx0ZXJpbmciLCJyZWQiKWAsIGEgd2VsbC1lc3RhYmxpc2hlZCBmaWVsZCBpbiAqKnNpZ25hbCBwcm9jZXNzaW5nKiouCgotICAgYHIgY29sb3JpemUoImZpbHRlcmluZyIsInJlZCIpYCBpcyBhIG1ldGhvZCBvciBwcm9jZXNzIHRoYXQgKipyZW1vdmVzKiogY2VydGFpbiB1bndhbnRlZCBpbmZvcm1hdGlvbiBmcm9tIGEgc2lnbmFsIG9yIGFuIGltYWdlLCBvciBhbHRlcm5hdGl2ZWx5ICoqZW5oYW5jZXMgaXQgYnkgYWNjZW50dWF0aW5nKiogY2VydGFpbiBpbmZvcm1hdGlvbi4KCi0gICBgciBjb2xvcml6ZSgiZmlsdGVyaW5nIiwicmVkIilgIGFwcGxpZXMgbWF0aGVtYXRpY2FsIG9wZXJhdGlvbnMgb24gaW5wdXQgaW1hZ2VzLCB3aXRoIHRoZSBtb3N0IGNvbW1vbiBvcGVyYXRpb24gYmVpbmcgdGhlIGByIGNvbG9yaXplKCJjb252b2x1dGlvbiIsInJlZCIpYC4gQSBjb252b2x1dGlvbiBjYW4gYmUgdmlld2VkIGFzIGFuIG9wZXJhdGlvbiBiZXR3ZWVuIHR3byBtYXRoZW1hdGljYWwgb2JqZWN0cywgc3VjaCBhcyAqKnR3byBtYXRyaWNlcyoqLCB3aGVyZSBvbmUgcmVwcmVzZW50cyAqKmFuIGltYWdlKiogYW5kIHRoZSBvdGhlciAqKmEgZmlsdGVyKiouCgotICAgRWFjaCBmaWx0ZXIgaXMgb2Z0ZW4gY3VzdG9tIGRlc2lnbmVkIGRlcGVuZGluZyBvbiB0aGUgc3BlY2lmaWMgdGFzayBhdCBoYW5kLiBGb3IgZXhhbXBsZSwgYSBwb3B1bGFyIGZpbHRlciwgY2FsbGVkIHRoZSBgciBjb2xvcml6ZSgiU29iZWwgZmlsdGVyIiwicmVkIilgLCBpcyB1c2VmdWwgZm9yIHRoZSB0YXNrIG9mICoqZWRnZSBkZXRlY3Rpb24qKi4gU2VlIHRoZSBmb2xsb3dpbmcgZmlndXJlLCB3aGVyZSBpbiAoYikgd2UgZGV0ZWN0IHRoZSBgciBjb2xvcml6ZSgidmVydGljYWwgZWRnZXMiLCJibHVlIilgIGFuZCBpbiAoYykgd2UgZGV0ZWN0IHRoZSBgciBjb2xvcml6ZSgiaG9yaXpvbnRhbCBlZGdlcyIsImJsdWUiKWAuIEJ5IGFkZGluZyB0aGUgb3V0cHV0cyBvZiB0aGVzZSB0d28gZmlsdGVyaW5nIG9wZXJhdGlvbnMsIHdlIGdldCB0aGUgZmluYWwgaW1hZ2Ugc2hvd24gaW4gKGQpIHdoaWNoIGNhcHR1cmVzIG1vc3Qgb2YgdGhlIHZlcnRpY2FsIGFuZCBob3Jpem9udGFsIGVkZ2UgaW5mb3JtYXRpb24uCgo8Y2VudGVyPgo8ZmlndXJlPgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9FZGdlLURldGVjdGlvbi5wbmciIHN0eWxlPSJ3aWR0aDo3MDBweDsiLz4KPC9maWd1cmU+CjwvY2VudGVyPgoKXAoKOjo6IGJsdWUKCioqTm90ZToqKiBDb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3JrcyBidWlsZCB1cG9uIHRoZSBjbGFzc2ljIGlkZWFzIG9mIGZpbHRlcmluZyB1c2luZyBgciBjb2xvcml6ZSgiY29udm9sdXRpb25hbCBsYXllcnMiLCJyZWQiKWAuIEVhY2ggY29udm9sdXRpb25hbCBsYXllciBpcyBtYWRlIHVwIG9mICBgciBjb2xvcml6ZSgib25lIG9yIG1vcmUgZmlsdGVycyIsImJsdWUiKWAsIGFsc28ga25vd24gYXMgYHIgY29sb3JpemUoImtlcm5lbHMiLCJyZWQiKWAsIGVhY2ggb2Ygd2hpY2ggYWltcyB0byBleHRyYWN0IGEgcGFydGljdWxhciBmZWF0dXJlIG9mIHRoZSBpbnB1dCB0byB0aGUgbGF5ZXIuCjo6OgoKIyMjIEV4YW1wbGUgQ05OIE5ldHdvcms6IFZHRzE5CgpgYGB7PWh0bWx9CjxzdHlsZT4KZGl2LmJsdWUgeyBiYWNrZ3JvdW5kLWNvbG9yOiNlNmYwZmY7IGJvcmRlci1yYWRpdXM6IDVweDsgcGFkZGluZzogMjBweDt9Cjwvc3R5bGU+CmBgYAoKLSAgIFRvIGdldCBhIGZlZWwgZm9yIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzIGxldCB1cyBjb25zaWRlciB0aGUgdGFzayBvZiAqKmNsYXNzaWZ5aW5nIGNvbG9yIGltYWdlcyoqIHVzaW5nIHRoZSBWR0cxOSBtb2RlbCAoVkdHIHN0YW5kcyBmb3IgYHIgY29sb3JpemUoIlZpc3VhbCBHZW9tZXRyeSBHcm91cCIsInJlZCIpYCwgdGhlIGdyb3VwIGF0IE94Zm9yZCBVbml2ZXJzaXR5IHRoYXQgY3JlYXRlZCB0aGUgbmV0d29yaykuCi0gICBUaGlzIG5ldHdvcmsgaXMgZGVzaWduZWQgdG8gdHJhaW4gb24gaW1hZ2VzIGluICoqSW1hZ2VOZXQqKiB3aGVyZSB0aGUgZGltZW5zaW9uIG9mIGVhY2ggaW1hZ2UgaXMgJDMgXHRpbWVzIDIyNCBcdGltZXMgMjI0JCwgaGVyZSAkMyQgaXMgdGhlIG51bWJlciBvZiBjaGFubmVscyAocmVkLCBncmVlbiwgYW5kIGJsdWUpLCBhbmQgJDIyNFx0aW1lcyAyMjQkIHNwZWNpZmllcyB0aGUgcGl4ZWwgZGltZW5zaW9ucy4gCi0gU2luY2UgdGhlcmUgYXJlICQxLDAwMCQgY2xhc3NlcyBpbiB0aGUgSW1hZ2VOZXQsIHRoZSBvdXRwdXQgb2YgVkdHMTkgaXMgYSBgciBjb2xvcml6ZSgicHJvYmFiaWxpdHkgdmVjdG9yICIsInJlZCIpYCBvZiBsZW5ndGggJDEsMDAwJC4KLSAgIFRoZSAqKlZHRzE5IG1vZGVsKiogaGFzIGFib3V0ICoqMTQ0KiogbWlsbGlvbiBwYXJhbWV0ZXJzCgoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vVkdHMTkucG5nIiBzdHlsZT0id2lkdGg6NzAwcHg7Ii8+CjwvZmlndXJlPgo8L2NlbnRlcj4KCgpgYGB7PWh0bWx9CjxzdHlsZT4KZGl2Lm9yYW5nZSB7IGJhY2tncm91bmQtY29sb3I6I0ZGRDU4MDsgYm9yZGVyLXJhZGl1czogNXB4OyBwYWRkaW5nOiAyMHB4O30KPC9zdHlsZT4KYGBgCgo6Ojogb3JhbmdlCioqUHJhY3RpY2FsIDE6KioKCi0gICBPcGVuIFR1dG9yaWFsIDEgb2YgQ05OIGF2YWlsYWJsZSBvbiB0aGUgd29ya3Nob3AgR2l0SHViIHBhZ2UuIEFsdGVybmF0aXZlbHksIGNsaWNrIDxhIGhyZWY9Imh0dHBzOi8vY29sYWIucmVzZWFyY2guZ29vZ2xlLmNvbS9kcml2ZS8xcGV2blh4emxXTGQ1X0JhbVdhSXpwQ1V4YzVOUnhrWEY/dXNwPXNoYXJpbmcidGFyZ2V0PSJfYmxhbmsiPmhlcmU8L2E+LgoKLSAgIFNhdmUgYSBjb3B5IG9mIHRoaXMgaW4geW91ciBHb29nbGUgQ29sYWIuCgotICAgSW4gdGhpcyBleGVyY2lzZSwgd2UgaW5wdXQgdHdvIGltYWdlcywgd2hpY2ggYXJlIG5vdCBwYXJ0IG9mIHRoZSBJbWFnZU5ldCBkYXRhc2V0LCB0byBzZWUgd2hhdCBpcyB0aGUgb3V0cHV0IG9mIGEgcHJldHJhaW5lZCBWR0cxOS4KCi0gICBOb3RlIHRoYXQgdHJhaW5pbmcgb2YgVkdHMTkgdGFrZXMgbWFueSBkYXlzIG9uIGEgcmVndWxhciBjb21wdXRlciwgYW5kIGhlbmNlIHdlIHVzZSBhIHByZXRyYWluZWQgbmV0d29yay4KOjo6CgojIFRoZSBDb252b2x1dGlvbiBPcGVyYXRpb24KCi0gICBBcyBtZW50aW9uZWQgZWFybGllciwgdGhlIGByIGNvbG9yaXplKCJjb252b2x1dGlvbiBvcGVyYXRpb24iLCJyZWQiKWAgaXMgYSBrZXkgY29tcG9uZW50IG9mIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzLgoKLSAgIEEgY29udm9sdXRpb24gY2FuIGJlIHZpZXdlZCBhcyBhbiBvcGVyYXRpb24gb24gdHdvIGZ1bmN0aW9ucyB3aGljaCBjcmVhdGVzIGEgdGhpcmQgZnVuY3Rpb24uIEluIGZpbml0ZSBkb21haW5zLCB0aGVzZSBmdW5jdGlvbnMgbWF5IGJlIHJlcHJlc2VudGVkIGFzIHZlY3RvcnMsIG1hdHJpY2VzLCBvciB0ZW5zb3JzLgoKLSAgIFRoZSBgciBjb2xvcml6ZSgiY29udm9sdXRpb24gb3BlcmF0aW9uIiwicmVkIilgIGJldHdlZW4gdHdvIG1hdHJpY2VzICRXJCBhbmQgJHgkIGlzIGRlbm90ZWQgYnkgICRXXHN0YXIgeCQuIFN1cHBvc2UgJEtfaCBcdGltZXMgS192JCBhbmQgJE1faCBcdGltZXMgTV92JCBhcmUgdGhlIGRpbWVuc2lvbnMgb2YgJFckIGFuZCAkeCQsIHJlc3BlY3RpdmVseS4gCgotICBJbiB0aGUgY29udGV4dCBvZiBDTk5zLCAkVyQgcmVwcmVzZW50cyBhIGByIGNvbG9yaXplKCJrZXJuZWwgYXQgYSBsYXllciIsImJsdWUiKWAgYW5kICR4JCByZXByZXNlbnRzIGFuIGByIGNvbG9yaXplKCJpbnB1dCB0byB0aGF0IGxheWVyIiwiYmx1ZSIpYC4gRnVydGhlciwgaXQgaXMgdXN1YWwgdGhhdCAkS19oIFxsZXEgTV9oJCBhbmQgJEtfdiBcbGVxIE1fdiQuCgo6OjogYmx1ZQoKLSAgIFRoZSBjb252b2x1dGlvbiAkV1xzdGFyIHgkIGlzIGEgbWF0cml4IG9mIGRpbWVuc2lvbiAkKE1faCAtIEtfaCArIDEpIFx0aW1lcyAoTV92IC0gS192ICsgMSksJCB3aGVyZSBmb3Igb3V0cHV0IGF0ICRpJyA9IDEsIFxkb3RzLCBNX2ggLSBLX2ggKyAxJCBhbmQgJGonID0gMSwgXGRvdHMsIE1fdiAtIEtfdiArIDEkLCB0aGUgY29udm9sdXRpb24gYWN0aW9uIGlzLAoKYGBgez10ZXh9ClxiZWdpbntlcXVhdGlvbn0Kel97aScsaid9ID0gKFcgXHN0YXIgeClfe2knLFwsIGonIH0gPSBcc3VtX3tpID0gMH1ee0tfaC0xfSAgXHN1bV97aiA9IDB9XntLX3YtMX0gd197S19oICAtaSAsIFwsIEtfdiAtIGp9IH4geF97aScgKyBpICxcLCAgaicgICtqfSwKXGVuZHtlcXVhdGlvbn0KYGBgCgo6OjoKCi0gICBUaGlzIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiBpcyBpbGx1c3RyYXRlZCBiZWxvdyB3aGVyZSAkVyQgaXMgZGltZW5zaW9uICQzXHRpbWVzIDMkIGFuZCAkeCQgaXMgZGltZW5zaW9uICQ2XHRpbWVzIDckLiBUaHVzICRXXHN0YXIgeCQgaXMgZGltZW5zaW9uICQoNi0zKzEpXHRpbWVzKDctMysxKSQKCmBgYHs9aHRtbH0KPCEtLQojIGBgYHtyIGNvbnZvbHV0aW9uLCBlY2hvPUZBTFNFLCBmaWcuc2hvdz0naG9sZCcsIG91dC53aWR0aCA9ICcxMzAlJywgZmlnLmNhcD0iQSBjYXB0aW9uIiwgZmlnLnBvcz0iSCIsIGZpZy5hbGlnbj0nY2VudGVyJ30KIyBrbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaW1hZ2VzLWNubi9maWd1cmUtNi4zYS5wbmciKQojIGBgYAotLT4KYGBgCjxjZW50ZXI+Cgo8ZmlndXJlPgoKPGltZyBzcmM9ImltYWdlcy1jbm4vZmlndXJlLTYuM2EucG5nIiBzdHlsZT0id2lkdGg6NzAwcHg7Ii8+Cgo8ZmlnY2FwdGlvbj5Db252b2x1dGlvbiBvcGVyYXRpb24uPC9maWdjYXB0aW9uPgoKPC9maWd1cmU+Cgo8L2NlbnRlcj4KClwKCi0gICBJbiBwYXJ0aWN1bGFyLCB0aGUgZmlyc3QgZWxlbWVudCBvZiB0aGUgb3V0cHV0IG1hdHJpeCAkeiQgaXMgY29tcHV0ZWQgYXMgc2hvd24gaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuCgo8Y2VudGVyPgoKPGZpZ3VyZT4KCjxpbWcgc3JjPSJpbWFnZXMtY25uL2ZpZ3VyZS02LjNiLnBuZyIgc3R5bGU9IndpZHRoOjcwMHB4OyIvPgoKPGZpZ2NhcHRpb24+Q29tcHV0aW5nICR6X3sxLDF9JC4gSGVyZSwgJFxvZG90JCBkZW5vdGVzIGVsZW1lbnQtd2lzZSBwcm9kdWN0IGJldHdlZW4gdHdvIG1hdHJpY2VzIGFuZCAkXHN1bSQgZGVub3RlcyBzdW1tYXRpb24gb2YgYWxsIHRoZSBlbGVtZW50cyBvZiBhIG1hdHJpeC48L2ZpZ2NhcHRpb24+Cgo8L2ZpZ3VyZT4KCjwvY2VudGVyPgoKIyMjIEVkZ2UgRGV0ZWN0aW9uCgpSZWNhbGwgdGhlIGVkZ2UgZGV0ZWN0aW9uIGV4YW1wbGUgcHJlc2VudGVkIGVhcmxpZXIuIFdlIGhhdmUgc2VlbiB0aGF0IGByIGNvbG9yaXplKCJib3RoIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGVkZ2VzIiwicmVkIilgIGFyZSBkZXRlY3RlZCB2aWEgYHIgY29sb3JpemUoImNvbnZvbHV0aW9uIG9wZXJhdGlvbiIsInJlZCIpYCB0byBvYnRhaW4gdGhlIGZpbmFsIG91dHB1dC4KCi0gICBUaGUgZmlsdGVyICRXJCBmb3IgZGV0ZWN0aW5nIHRoZSBob3Jpem9udGFsIGVkZ2VzIGlzIGdpdmVuIGJ5CgpgYGB7PXRleH0KXGJlZ2lue2VxdWF0aW9ufQogICAgIFxiZWdpbntibWF0cml4fQogICAgICAgIC0xICYgLTIgJiAtMVxcCiAgICAgICAgMCAmIDAgJiAwXFwKICAgICAgICAxICYgMiAmIDFcXAogICAgIFxlbmR7Ym1hdHJpeH0uClxlbmR7ZXF1YXRpb259CmBgYAotICAgVGhlIGZpbHRlciAkVyQgZm9yIGRldGVjdGluZyB0aGUgdmVydGljYWwgZWRnZXMgaXMgZ2l2ZW4gYnkgXGJlZ2lue2VxdWF0aW9ufSAKICAgICAgIFxiZWdpbntibWF0cml4fQogICAgICAgICAgLTEgJiAwICYgMVxcCiAgICAgICAgICAtMiAmIDAgJiAyXFwKICAgICAgICAgIC0xICYgMCAmIDFcXAogICAgICAgXGVuZHtibWF0cml4fS4KICAgIFxlbmR7ZXF1YXRpb259Cgo6OjogYmx1ZQoqKk5vdGUgMSoqOgoKLSBUaGUgZmlsdGVyaW5nIG9wZXJhdGlvbiBhdCBlYWNoIGxheWVyIGluIGEgQ05OIGlzIHNpbWlsYXIgdG8gY2xhc3NpY2FsIGZpbHRlcmluZywgc3VjaCBhcyBlZGdlIGRldGVjdGlvbiBhYm92ZS4gKipIb3dldmVyKiosIHVubGlrZSBjbGFzc2ljYWwgZmlsdGVyaW5nLCBmaWx0ZXJzIGluIENOTnMgYXJlIGByIGNvbG9yaXplKCJsZWFybmVkIiwicmVkIilgIHJhdGhlciB0aGFuICoqZGVzaWduZWQqKi4gCgotIEEgdHJhaW5pbmcgZGF0YXNldCBpcyB1c2VkIGZvciBsZWFybmluZyB0aGUgZmlsdGVycyBiZWZvcmUgdXNpbmcgdGhlIG5ldHdvcmsgZm9yIGltYWdlIHByb2Nlc3NpbmcuIEhlcmUsIGxlYXJuaW5nIGEgZmlsdGVyIG1lYW5zIGxlYXJuaW5nIHRoZSBlbnRyaWVzIG9mIHRoZSBtYXRyaXggdGhhdCByZXByZXNlbnRzIHRoZSBmaWx0ZXIsIGFuZCB0aGVzZSBlbnRyaWVzIGFyZSBjYWxsZWQgKip3ZWlnaHRzKiogYXMgdGhlIGZpbHRlcnMgcGxheSBhIHNpbWlsYXIgcm9sZSB0byB0aGUgd2VpZ2h0IG1hdHJpY2VzIG9mIGEgZnVsbHkgY29ubmVjdGVkIG5ldXJhbCBuZXR3b3Jrcy4KOjo6CgpcCgo6OjogYmx1ZQoKKipOb3RlIDIqKjogU2luY2UgZmlsdGVycyBpbiBDTk5zIGFyZSBsZWFybmVkIHJhdGhlciB0aGFuIGRlc2lnbmVkLCB0aGVyZSBpcyAqKm5vIG5lZWQgb2YgZmxpcHBpbmcgb3BlcmF0aW9uKiosIGluc3RlYWQsIHdlIGRpcmVjdGx5IGxlYXJuIHRoZSBmbGlwcGVkIGZpbHRlci4KOjo6CgojIEJ1aWxkaW5nIGEgQ29udm9sdXRpb25hbCBMYXllcgoKLSAgIEluIHRoZSBlYXJsaWVyIHBhcnQgb2YgdGhpcyB3b3Jrc2hvcCwgd2UgaGF2ZSBzZWVuIHRoZSBjb25zdHJ1Y3Rpb24gb2YgYHIgY29sb3JpemUoImdlbmVyYWwgZnVsbHkgY29ubmVjdGVkIG5ldXJhbCBuZXR3b3JrcyIsInJlZCIpYCwgZWFjaCBvZiB3aGljaCBjb25zaXN0cyBvZiBhIHNlcmllcyBvZiBsYXllcnMgd2hlcmUgZXZlcnkgbmV1cm9uIGluIGEgZ2l2ZW4gbGF5ZXIgaXMgY29ubmVjdGVkIHRvIGV2ZXJ5IG5ldXJvbiBpbiB0aGUgbmV4dCBsYXllci4KCi0gICBGdWxseSBjb25uZWN0ZWQgbmV0d29ya3MgYXJlIGdlbmVyYWwgaW4gdGhlIHNlbnNlIHRoYXQgdGhleSBhcmUgYHIgY29sb3JpemUoInN0cnVjdHVyZSBhZ25vc3RpYyIsInJlZCIpYCwgdGhhdCBpcywgdGhlcmUgYXJlICoqbm8gc3BlY2lmaWMgYXNzdW1wdGlvbnMqKiBtYWRlIGFib3V0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGlucHV0LiBUaGlzIHByb3BlcnR5IG1ha2VzIGZ1bGx5IGNvbm5lY3RlZCBuZXVyYWwgbmV0d29ya3MgYHIgY29sb3JpemUoInZlcnNhdGlsZSIsImJsdWUiKWAuCgotICAgSG93ZXZlciwgZnVsbHkgY29ubmVjdGVkIG5ldHdvcmtzIGFyZSBgciBjb2xvcml6ZSgiaW5hZGVxdWF0ZSIsInJlZCIpYCB3aGVuIGRlYWxpbmcgd2l0aCB0aGUgaW5wdXQgdGhhdCBoYXMgYHIgY29sb3JpemUoInJpY2ggc3RydWN0dXJhbCBwcm9wZXJ0aWVzIiwiYmx1ZSIpYCBzdWNoIGFzIGltYWdlcy4KCi0gICBDb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3JrcyBtYWtlIHVzZSBvZiB0aGUgYWZvcmVtZW50aW9uZWQgdHdvIGtleSBwcm9wZXJ0aWVzIG9mIGdyaWQtc3RydWN0dXJlZCBkYXRhLCBuYW1lbHkgKip0cmFuc2xhdGlvbiBpbnZhcmlhbmNlKiogYW5kICoqbG9jYWxpdHkqKi4gCgotIEFzIGEgcmVzdWx0LCB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgdG8gbGVhcm4gaW4gY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MgaXMgYHIgY29sb3JpemUoInNpZ25pZmljYW50bHkgc21hbGxlciIsImJsdWUiKWAgdGhhbiB0aGF0IG9mIGNvcnJlc3BvbmRpbmcgZnVsbHkgY29ubmVjdGVkIG5ldXJhbCBuZXR3b3Jrcy4KCiMjIyBDYW4gd2UgdXNlIGZ1bGx5IGNvbm5lY3RlZCBuZXR3b3JrcyBmb3IgaW1hZ2UgZGF0YT8KCkFuc3dlcjogYHIgY29sb3JpemUoIlllcyEiLCJyZWQiKWAKCi0gICBUbyB1c2UgZnVsbHkgY29ubmVjdGVkIG5ldXJhbCBuZXR3b3JrcyBvbiBpbWFnZXMgJHgkIG9mIGRpbWVuc2lvbiAkTV9oXntbMF19IFx0aW1lcyBNX3Zee1swXX0kLCB0aGV5IGFyZSBmaXJzdCBjb252ZXJ0ZWQgdG8gYSB2ZWN0b3Igb2YgbGVuZ3RoICRNX2hee1swXX1cY2RvdCBNX3Zee1swXX0kIGluIGEgY29uc2lzdGVudCBtYW5uZXIuCgotICAgV2l0aG91dCBsb3NzIG9mIGdlbmVyYWxpdHkgd2UgY2FuIGNvbnRpbnVlIHRvIGluZGV4IHRoZSBlbGVtZW50cyBvZiB0aGUgdmVjdG9yICR4JCB2aWEgdHVwbGVzICQoaSwgaikgXGluIFx7MSwgXGRvdHMsIE1faF57WzBdfVx9IFx0aW1lcyBcezEsIFxkb3RzLCBNX3Zee1swXX1cfSQuCgotICAgVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgbWF0cml4IHRvIHZlY3RvciBjb252ZXJzaW9uIGJlZm9yZSBpbnB1dHRpbmcgaXQgdG8gYSBmdWxseSBjb25uZWN0ZWQgbGF5ZXIuCgo8Y2VudGVyPgoKPGZpZ3VyZSBpZD0ibWF0cml4LXRvLXZlYyI+Cgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9NYXRyaXgtdG8tVmVjLXRvLWZjLnBuZyIgYWx0PSJtYXRyaXgtdG8tdmVjIiBzdHlsZT0id2lkdGg6NjAwcHg7aGVpZ2h0OjUwMHB4OyIvPgoKPGZpZ2NhcHRpb24+QSBjb25zaXN0ZW50IG1hdHJpeC10by12ZWN0b3IgY29udmVyc2lvbiBvcGVyYXRpb24uPC9maWdjYXB0aW9uPgoKPC9maWd1cmU+Cgo8L2NlbnRlcj4KCmBgYHs9aHRtbH0KPCEtLQohW10oaW1hZ2VzLWNubi9NYXRyaXgtdG8tVmVjLXRvLWZjLnBuZykgCi0tPgpgYGAKwqBcCgotICAgVGhlIGZvbGxvd2luZyBmaWd1cmUgc2hvd3MgcmVjb25zdHJ1Y3Rpb24gb2YgdGhlIG1hdHJpeCBmcm9tIHRoZSBjb3JyZXNwb25kaW5nIHZlY3Rvci4gVGhpcyBvcGVyYXRpb24gY2FuIGJlIHVzZWQgb24gb3V0cHV0cyBvZiBhIGZ1bGx5IGNvbm5lY3RlZCBsYXllciBpZiB0aGUgbGVuZ3RoIG9mIHRoZSBvdXRwdXQgaXMgYSBwcm9kdWN0IG9mIHR3byBpbnRlZ2Vycy4KCjxjZW50ZXI+Cgo8ZmlndXJlPgoKPGltZyBzcmM9ImltYWdlcy1jbm4vRmMtdG8tVmVjLXRvLU1hdHJpeC5wbmciIGFsdD0idmVjLXRvLW1hdHJpeCIgc3R5bGU9IndpZHRoOjYwMHB4O2hlaWdodDo1MDBweDsiLz4KCjxmaWdjYXB0aW9uPlJlcHJvZHVjaW5nIG1hdHJpeCBmcm9tIHRoZSB2ZWN0b3IuPC9maWdjYXB0aW9uPgoKPC9maWd1cmU+Cgo8L2NlbnRlcj4KCsKgIMKgCgo6OjogYmx1ZQoqKklzc3VlczoqKiAKCi0gMSkgV2UgbG9zZSBib3RoIHRoZSBzdHJ1Y3R1cmFsIHByb3BlcnRpZXM6IHRyYW5zbGF0aW9uIGludmFyaWFuY2UgYW5kIGxvY2FsaXR5LiAKCi0gMikgVG9vIG1hbnkgcGFyYW1ldGVycyB0byBsZWFybi4KCjo6OgoKIyMjIE1vdGl2YXRpbmcgYSBDb252b2x1dGlvbmFsIExheWVyIChTaW1wbGlmaWVkKQoKMS4gKipCdWlsZGluZyBhIExheWVyIHdpdGggTG9jYWwgVW5kZXJzdGFuZGluZyoqOiAgCiAgIEluIGEgZnVsbHkgY29ubmVjdGVkIGxheWVyLCBldmVyeSBuZXVyb24gY29ubmVjdHMgdG8gZXZlcnkgaW5wdXQsIGJ1dCBpbWFnZXMgaGF2ZSAqKnNwYXRpYWwgc3RydWN0dXJlKiouIEluIGNvbnZvbHV0aW9uYWwgbGF5ZXJzLCB3ZSBidWlsZCBhIHN5c3RlbSB0aGF0IG9ubHkgbG9va3MgYXQgYHIgY29sb3JpemUoImxvY2FsIHJlZ2lvbnMiLCJyZWQiKWBpbiB0aGUgaW1hZ2UsIHByb2Nlc3Npbmcgc21hbGwgYXJlYXMgYXQgYSB0aW1lLiBUaGlzIGxvY2FsIGZvY3VzIGhlbHBzIHVzIGNhcHR1cmUgYHIgY29sb3JpemUoIm1lYW5pbmdmdWwgcGF0dGVybnMiLCJyZWQiKWAsIHN1Y2ggYXMgZWRnZXMgb3IgdGV4dHVyZXMsIHRoYXQgYXJlIGVzc2VudGlhbCBmb3IgdW5kZXJzdGFuZGluZyBpbWFnZXMuCgoyLiAqKlJlZHVjaW5nIFBhcmFtZXRlcnMgd2l0aCBSZXBlYXRlZCBQYXR0ZXJucyoqOiAgCiAgIENvbnZvbHV0aW9uYWwgbGF5ZXJzIHJlZHVjZSB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgYnkgYXBwbHlpbmcgdGhlIHNhbWUgYHIgY29sb3JpemUoImZpbHRlciIsImJsdWUiKWAgYWNyb3NzIHRoZSBpbWFnZS4gVGhpcyBgciBjb2xvcml6ZSgiZmlsdGVyIiwiYmx1ZSIpYCBpcyBsaWtlIGEgc21hbGwgd2luZG93IHRoYXQgKipzbGlkZXMgb3ZlciB0aGUgaW1hZ2UqKiwgZXhhbWluaW5nIG9uZSBzbWFsbCByZWdpb24gYXQgYSB0aW1lLiBVc2luZyBhIHNpbmdsZSBgciBjb2xvcml6ZSgiZmlsdGVyIiwiYmx1ZSIpYCByZXBlYXRlZGx5IG1lYW5zIHdlIGRvbuKAmXQgbmVlZCBhIG5ldyBzZXQgb2YgcGFyYW1ldGVycyBmb3IgZXZlcnkgcmVnaW9uLCBtYWtpbmcgdGhlIG5ldHdvcmsgbW9yZSBlZmZpY2llbnQuIEl0IGFsc28gZ2l2ZXMgdGhlIG5ldHdvcmsgYSB0eXBlIG9mICoqbWVtb3J5KiosIGhlbHBpbmcgaXQgcmVjb2duaXplIHBhdHRlcm5zIGFjcm9zcyB0aGUgaW1hZ2UuCgozLiAqKlRyYW5zbGF0aW9uIEludmFyaWFuY2UqKjogIAogICBXaGVuIHdlIGFwcGx5IGEgYHIgY29sb3JpemUoImZpbHRlciIsImJsdWUiKWAgYWNyb3NzIHRoZSBlbnRpcmUgaW1hZ2UsIHRoZSBuZXR3b3JrIGdhaW5zIGByIGNvbG9yaXplKCJ0cmFuc2xhdGFpb24gaW52YXJpYW5jZSIsInJlZCIpYC4gVGhpcyBtZWFucyBpdCBjYW4gcmVjb2duaXplIHBhdHRlcm5zLCBzdWNoIGFzIHRoZSBlZGdlIG9mIGFuIG9iamVjdCwgbm8gbWF0dGVyIHdoZXJlIHRoZXkgYXBwZWFyIGluIHRoZSBpbWFnZS4gRm9yIGluc3RhbmNlLCBpZiBhIG5ldHdvcmsgbGVhcm5zIHRvIGRldGVjdCBlZGdlcyBvciBzaGFwZXMsIGl0IGRvZXNu4oCZdCBtYXR0ZXIgaWYgdGhvc2Ugc2hhcGVzIGFyZSBpbiB0aGUgKip0b3AgbGVmdCoqIG9yICoqYm90dG9tIHJpZ2h0KirigJR0aGlzIGByIGNvbG9yaXplKCJpbnZhcmlhbmNlIiwicmVkIilgIGxldHMgaXQgcmVjb2duaXplIG9iamVjdHMgcmVnYXJkbGVzcyBvZiBwb3NpdGlvbi4gQXMgYW4gaWxsdXN0cmF0aW9uLCBsZXQgdXMgcmV2aXNpdCBlZGdlIGRldGVjdGlvbiBhbmQgY29uc2lkZXIgYSBwZWxpY2FuIGluIGZsaWdodCBhcyBzaG93biBpbiBmb2xsb3dpbmcgZmlndXJlcy4KCi0gICBGaWd1cmUgKGEpOgoKPGNlbnRlcj4KCjxmaWd1cmU+Cgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9CaXJkX2ZseV90b3BfYncuanBnIiBhbHQ9IkJpcmQiIHN0eWxlPSJ3aWR0aDozMDBweDtoZWlnaHQ6MjAwcHg7Ii8+IDxpbWcgc3JjPSJpbWFnZXMtY25uL0JpcmRfZmx5X2VkZ2VfdG9wLmpwZyIgYWx0PSJCaXJkIiBzdHlsZT0id2lkdGg6MzAwcHg7aGVpZ2h0OjIwMHB4OyIvPgoKPGZpZ2NhcHRpb24+QW4gaW5wdXQgYW5kIHRoZSBjb3JyZXNwb25kaW5nIG91dHB1dCBvZiBlZGdlIGRldGVjdGlvbi48L2ZpZ2NhcHRpb24+Cgo8L2ZpZ3VyZT4KCjwvY2VudGVyPgoKXApcCgotICAgRmlndXJlIChiKToKCjxjZW50ZXI+Cgo8ZmlndXJlPgoKPGltZyBzcmM9ImltYWdlcy1jbm4vQmlyZF9mbHlfYm90dG9tX2J3LmpwZyIgYWx0PSJCaXJkIiBzdHlsZT0id2lkdGg6MzAwcHg7aGVpZ2h0OjIwMHB4OyIvPiA8aW1nIHNyYz0iaW1hZ2VzLWNubi9CaXJkX2ZseV9lZGdlX2JvdHRvbS5qcGciIGFsdD0iQmlyZCIgc3R5bGU9IndpZHRoOjMwMHB4O2hlaWdodDoyMDBweDsiLz4KCjxmaWdjYXB0aW9uPkFub3RoZXIgaW5wdXQgYW5kIHRoZSBjb3JyZXNwb25kaW5nIG91dHB1dCBvZiBlZGdlIGRldGVjdGlvbi48L2ZpZ2NhcHRpb24+Cgo8L2ZpZ3VyZT4KCjwvY2VudGVyPgoKXApcCgotICAgT2JzZXJ2ZSB0aGF0IEZpZ3VyZXMgKGEpIGFuZCAoYikgYXJlIGVzc2VudGlhbGx5IHRoZSBzYW1lLCBleGNlcHQgZm9yIHRoZSBmYWN0IHRoYXQgdGhlIHBvc2l0aW9uIG9mIHRoZSBwZWxpY2FuIGluIHRoZSBvdXRwdXQgYHIgY29sb3JpemUoImRlcGVuZHMgb25seSBvbiBpdHMgcG9zaXRpb24gaW4gdGhlIGlucHV0IiwicmVkIilgLiBJbiBvdGhlciB3b3JkcywgdGhlIGZpbHRlcmluZyBvcGVyYXRpb24ncyBhY3Rpb24gb24gdGhlIG9iamVjdCBpcyBnZW5lcmFsbHkgaW5kZXBlbmRlbnQgb2YgdGhlIGxvY2F0aW9uIG9mIHRoZSBvYmplY3QgaW4gdGhlIGltYWdlLgoKCjQuICoqTG9jYWxpdHkgZm9yIEVmZmljaWVudCBSZWNvZ25pdGlvbioqOiAgCiBXZSBub3cgc2VlIGZ1cnRoZXIgcmVkdWN0aW9uIG9mIHRoZSBwYXJhbWV0ZXJzIGJ5IGludm9raW5nIHRoZSBzZWNvbmQgc3RydWN0dXJhbCBwcm9wZXJ0eSwgbmFtZWx5IGByIGNvbG9yaXplKCJsb2NhbGl0eSIsInJlZCIpYC4gQ29udm9sdXRpb25hbCBsYXllcnMgYXNzdW1lIHRoYXQgYHIgY29sb3JpemUoIm5lYXJieSBwaXhlbHMiLCJibHVlIilgIGFyZSBtb3JlIHJlbGV2YW50IHRvIGVhY2ggb3RoZXIgdGhhbiBkaXN0YW50IG9uZXMuIEluIG90aGVyIHdvcmQsIGEgcGl4ZWwgJHhfe2ksan0kIGlzIGByIGNvbG9yaXplKCJub3Qgc2lnbmlmaWNhbnRseSBpbmZsdWVuY2VkIGJ5IGZhciBhd2F5IHBpeGVscyIsImJsdWUiKWAuIEEgbW90aXZhdGlvbmFsIGlsbHVzdHJhdGlvbiBpcyBpbiB0aGUgZm9sbG93aW5nIGZpZ3VyZXMgY29uc2lzdGluZyBvZiBwZWxpY2FucyBhbmQgc2VhZ3VsbHMsIHdpdGggZWFjaCBpbmRpdmlkdWFsIGJpcmQgZW5jbG9zZWQgaW4gYSByZWQgYm94LgogIAoKXAoKPGNlbnRlcj4KCjxmaWd1cmU+Cgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9CaXJkX2ZseS5qcGciIGFsdD0iQmlyZCIgc3R5bGU9IndpZHRoOjMwMHB4O2hlaWdodDoyMDBweDsiLz4gPGltZyBzcmM9ImltYWdlcy1jbm4vQmlyZF9ncm91bmQuanBnIiBhbHQ9IkJpcmQiIHN0eWxlPSJ3aWR0aDozMDBweDtoZWlnaHQ6MjAwcHg7Ii8+Cgo8ZmlnY2FwdGlvbj5JbWFnZXMgb2YgYmlyZHMgdG8gaWxsdXN0cmF0ZSB0aGUgcHJvcGVydHkgb2YgbG9jYWxpdHkuIFRoZSBwaXhlbCBpbmZvcm1hdGlvbiB3aXRoaW4gZWFjaCByZWQgYm94IGlzIHN1ZmZpY2llbnQgdG8gdW5kZXJzdGFuZCB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHRoZSBiaXJkIGluc2lkZSB0aGUgYm94LjwvZmlnY2FwdGlvbj4KCjwvZmlndXJlPgoKPC9jZW50ZXI+CgpcClwKCi0gICBgciBjb2xvcml6ZSgiTG9jYWxpdHkiLCJyZWQiKWAgaW1wbGllcyB0aGF0IGlmIHdlIGFyZSBzZWVraW5nIGluZm9ybWF0aW9uIGFib3V0IGEgYmlyZCwgdGhlbiBpdCBpcyBzdWZmaWNpZW50IHRvIGtub3cgdGhlIHBpeGVsIGluZm9ybWF0aW9uIG9ubHkgd2l0aGluIHRoZSBib3ggdGhhdCBpcyBlbmNsb3NpbmcgdGhlIGJpcmQuIFNpbWlsYXJseSwgYXQgYSBtdWNoIGZpbmVyIGxldmVsIHdoZW4gd2Ugc2VlayBpbmZvcm1hdGlvbiBhYm91dCBlZGdlcyBvciBzaW1pbGFyIGZlYXR1cmVzLCBpdCBpcyBgciBjb2xvcml6ZSgib2Z0ZW4gZW5vdWdoIHRvIGNvbnNpZGVyIDEsIDIsIG9yIDMsIG5laWdoYm9yaW5nIHBpeGVscyBpbiBlYWNoIGRpcmVjdGlvbiIsInJlZCIpYCAtLSB5aWVsZGluZyBjb252b2x1dGlvbiBrZXJuZWxzIG9mIHNpemUgJDNcdGltZXMgMyQsICQ1XHRpbWVzIDUkLCBvciAkN1x0aW1lcyA3JCByZXNwZWN0aXZlbHkuIFRoZXnigJlyZSBkZXNpZ25lZCB0byByZWNvZ25pemUgZGV0YWlscyBgciBjb2xvcml6ZSgid2l0aG91dCBiZWluZyBpbmZsdWVuY2VkIGJ5IGZhci1hd2F5IHBpeGVscyIsImJsdWUiKWAKCgo1LiAqKkJ1aWxkaW5nIENvbXBsZXggVW5kZXJzdGFuZGluZyBmcm9tIFNpbXBsZSBGaWx0ZXJzKio6ICAKICAgQnkgc3RhY2tpbmcgbXVsdGlwbGUgYHIgY29sb3JpemUoImNvbnZvbHV0aW9uYWwgbGF5ZXJzIiwiYmx1ZSIpYCwgZWFjaCBsYXllciBjYW4gY2FwdHVyZSBgciBjb2xvcml6ZSgiaW5jcmVhc2luZ2x5IGNvbXBsZXggZmVhdHVyZXMiLCJibHVlIilgLiBFYXJseSBsYXllcnMgbWlnaHQgZGV0ZWN0IHNpbXBsZSBlZGdlcywgd2hpbGUgbGF0ZXIgb25lcyByZWNvZ25pemUgcGFydHMgb2Ygb2JqZWN0cyBvciBldmVuIGVudGlyZSBvYmplY3RzLgoKCjo6OiBibHVlCgoKIyMjIEdlbmVyYWwgT3BlcmF0aW9uIG9mIGEgQ29udm9sdXRpb25hbCBMYXllcgoKVGhlIG9wZXJhdGlvbiBvZiB0aGUgbGF5ZXIgY2FuIGJlIHJlcHJlc2VudGVkIGFzCmBgYHs9dGV4fQpcYmVnaW57ZXF1YXRpb259CmFee1sxXX09U157WzFdfSAoel57WzFdfSkgXCBcIFwgXHRleHRybXt3aGVyZX1cIFwgXCBcIHpee1sxXX0gPSAoV1xzdGFyIHgpICsgYgpcZW5ke2VxdWF0aW9ufQpgYGAKCi0gJHgkIGlzIHRoZSBpbnB1dCBmZWF0dXJlIG1hcC4KLSAkVyQgaXMgdGhlIGNvbnZvbHV0aW9uIGByIGNvbG9yaXplKCJrZXJuZWwiLCAiYmx1ZSIpYCBvciBgciBjb2xvcml6ZSgiZmlsdGVyIiwgImJsdWUiKWAKLSAkYiQgaXMgdGhlIHNjYWxhciBiaWFzIGlzIGVsZW1lbnQtd2lzZSB0byBlYWNoIGVsZW1lbnQgb2YgdGhlIG1hdHJpeCB0ZXJtIGZvciBsYXllciAkV1xzdGFyIHgkLgotICRTXntbMV19KFxjZG90KSQgYW4gKiphY3RpdmF0aW9uIGZ1bmN0aW9uKiogKG9mdGVuIGEgYHIgY29sb3JpemUoIlJlTFUiLCAiYmx1ZSIpYCkgaXMgYXBwbGllZCB0byB0aGUgcmVzdWx0CiAgIAotLS0KCioqU3VtbWFyeToqKiBXZSBoYXZlIHNlZW4gdGhhdCBhdCBpdHMgY29yZSwgYSBzaW5nbGUgY29udm9sdXRpb25hbCBsYXllciBpbnZvbHZlcyB0aGUgZm9sbG93aW5nIGFjdGlvbnMgb24gdGhlIGlucHV0ICR4JC4gRmlyc3QgaXQgaXMgY29udm9sdmVkIHdpdGggYSBjb252b2x1dGlvbiBrZXJuZWwgJFckLiBUaGVuIHRoZSByZXN1bHQgaXMgc2hpZnRlZCBieSBhIHNjYWxhciBiaWFzICRiJC4gRmluYWxseSBhbiBhY3RpdmF0aW9uIGZ1bmN0aW9uICRTXntbMV19KFxjZG90KSQgaXMgYXBwbGllZC4gTm90ZSB0aGF0IHdoZW4gdGhlIGlucHV0IGRpbWVuc2lvbiBpcyAkTV9oXntbMF19IFx0aW1lcyBNX3Zee1swXX0kLCB0aGUgZGltZW5zaW9uIG9mIHRoZSBvdXRwdXQgaXMsCgpgYGB7PXRleH0KXGJlZ2lue2VxdWF0aW9ufQpNX2hee1sxXX0gXHRpbWVzIE1fdl57WzFdfSAKPSAKKE1faF57WzBdfSAtIEtfaCArIDEpIFx0aW1lcyAoTV92XntbMF19IC0gS192ICsgMSkuClxlbmR7ZXF1YXRpb259CmBgYAo6OjoKCsKgCgo6Ojogb3JhbmdlCgoqKkV4ZXJjaXNlOioqIApGb3IgYW4gaWxsdXN0cmF0aW9uIG9mIHRoZSByZWR1Y3Rpb24gaW4gdGhlIG51bWJlciBvZiBwYXJhbWV0ZXJzIHRoYXQgYSBjb252b2x1dGlvbmFsIGxheWVyIGhhcyBpbiBjb21wYXJpc29uIHRvIGEgZnVsbHkgY29ubmVjdGVkIGxheWVyLCBjb25zaWRlciBhbiBleGFtcGxlIHdpdGggaW5wdXQgZGltZW5zaW9uICRNX2hee1swXX0gXHRpbWVzIE1fdl57WzBdfSA9IDIyNCBcdGltZXMgMjI0JCBhbmQgYSBjYXNlIHdpdGgga2VybmVsIGRpbWVuc2lvbiAkS19oIFx0aW1lcyBLX3YgPSAzXHRpbWVzIDMkLiBUaGVuIHRoZSBvdXRwdXQgZGltZW5zaW9uIGlzICRNXntbMV19X2ggXHRpbWVzIE1ee1sxXX1fdiA9IDIyMiBcdGltZXMgMjIyJC4KCklmIHdlIHdlcmUgdG8gc2VlayB0aGUgc2FtZSBzaXplIG9mIG91dHB1dCBkaW1lbnNpb24gd2l0aCBhICoqZnVsbHkgY29ubmVjdGVkIGxheWVyKiosIHdlIGhhdmUgJDIyMlx0aW1lcyAyMjIgPSA0OSwyODQkIG5ldXJvbnMuIFNpbmNlIHRoZSBpbnB1dCBzaXplIGlzICQyMjRcdGltZXMgMjI0ID0gNTAsMTc2JCwgdGhlIGRpbWVuc2lvbiBvZiB0aGUgd2VpZ2h0IG1hdHJpeCBpcyB0aGUgcHJvZHVjdCBvZiB0aGUgaW5wdXQgc2l6ZSBhbmQgb3V0cHV0IHNpemUgKG51bWJlciBvZiBuZXVyb25zKSwgYW5kIHRvZ2V0aGVyIHdpdGggdGhlIGJpYXMgdmVjdG9yIChvbmUgZW50cnkgZm9yIGVhY2ggbmV1cm9uKSB3ZSBoYXZlICQyLDQ3Miw5MjMsMjY4JCBwYXJhbWV0ZXJzLgoKSW4gY29udHJhc3QsIGluIHRoZSBjb252b2x1dGlvbmFsIGxheWVyIHRoZXJlIGFyZSBvbmx5ICQzIFx0aW1lcyAzICsgMSA9IDEwJCBwYXJhbWV0ZXJzLiBXaGlsZSBvbiBpdHMgb3duLCBzdWNoIGEgc2luZ2xlIGNvbnZvbHV0aW9uIGxheWVyIGlzIGNlcnRhaW5seSBub3QgYXMgZXhwcmVzc2l2ZSBhcyB0aGUgZnVsbHkgY29ubmVjdGVkIGxheWVyIHdpdGggMi41IGJpbGxpb24gbGVhcm5lZCBwYXJhbWV0ZXJzLCBhcyB3ZSBzZWUgYmVsb3csIGNvbWJpbmluZyBjb252b2x1dGlvbmFsIGxheWVycyBpbiB0YW5kZW0geWllbGRzIHZlcnkgcG93ZXJmdWwgbmV0d29ya3Mgd2l0aCBtdWNoIGZld2VyIHBhcmFtZXRlcnMgdGhhbiB0aGVpciBmdWxseSBjb25uZWN0ZWQgY291bnRlcnBhcnRzLgo6OjoKCiMjIyBBbHRlcmF0aW9ucyB0byB0aGUgQ29udm9sdXRpb246IFBhZGRpbmcsIFN0cmlkZSwgYW5kIERpbGF0aW9uCgotICAgVGhlIGNvbnZvbHV0aW9uIGFwcGVhcmluZyBhYm92ZSBpcyBvZnRlbiB0d2Vha2VkIGFuZCBtb2RpZmllZCBpbiB0aGUgY29udGV4dCBvZiBpbWFnZSBkYXRhLgoKLSAgIFNwZWNpZmljYWxseSwgYWx0ZXJhdGlvbnMgdG8gdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiwga25vd24gYXMgYHIgY29sb3JpemUoInBhZGRpbmciLCJyZWQiKWAsIGByIGNvbG9yaXplKCJzdHJpZGUiLCJyZWQiKWAsIGFuZCBgciBjb2xvcml6ZSgiZGlsYXRpb24iLCJyZWQiKWAsIGFyZSBzb21ldGltZXMgZW1wbG95ZWQuCgotICAgV2Ugbm93IGludHJvZHVjZSBhbmQgaWxsdXN0cmF0ZSBlYWNoIG9mIHRoZXNlIGFsdGVyYXRpb25zIHNlcGFyYXRlbHkuCgoKIyMjIyBQYWRkaW5nCgotICAgUmVjYWxsIHRoZSBlZGdlIGRldGVjdGlvbiBleGFtcGxlIGFib3ZlLiBEdWUgdG8gdGhlIGNvbnZvbHV0aW9uIG9wZXJhdGlvbiwgYHIgY29sb3JpemUoInRoZSBvdXRwdXQgaW1hZ2UgZGltZW5zaW9uIGlzIHNtYWxsZXIgdGhhbiB0aGUgaW5wdXQgaW1hZ2UgZGltZW5zaW9uIiwiYmx1ZSIpYC4KCi0gICBJbiBwYXJ0aWN1bGFyLCBzaW5jZSB0aGUgZmlsdGVyIGRpbWVuc2lvbiAkS19oIFx0aW1lcyBLX3YkIGlzICQzIFx0aW1lcyAzJCAoU29iZWwgZmlsdGVyKSwgd2hlbiB0aGUgaW5wdXQgZGltZW5zaW9uIGlzICRNXntbMF19X2ggXHRpbWVzIE1ee1swXX1fdiQsIHRoZSBvdXRwdXQgZGltZW5zaW9uIGlzIGVxdWFsIHRvICQoTV57WzBdfV9oIC0yKSBcdGltZXMgKE1ee1swXX1fdiAtIDIpJC4KCi0gICBIZW5jZSB3ZSBzZWUgYSBzbGlnaHQgcmVkdWN0aW9uIG9mIHRoZSBpbWFnZSBzaXplIGF0IHRoZSBvdXRwdXQuIFNpbmNlIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzIHR5cGljYWxseSBjb25zaXN0IG9mIHNldmVyYWwgY29udm9sdXRpb24gbGF5ZXJzLCB0aGUgZGltZW5zaW9uIHJlZHVjdGlvbnMgaW4gZWFjaCBvZiB0aGVzZSBsYXllcnMgY2FuIGFjY3VtdWxhdGUsIG1ha2luZyB0aGUgb3ZlcmFsbCBkb3duc3RyZWFtIGRpbWVuc2lvbiAqKnVuZGVzaXJhYmx5IHNtYWxsKiouCgotICAgYHIgY29sb3JpemUoIlBhZGRpbmciLCJyZWQiKWAgaXMgYSBzaW1wbGUgc29sdXRpb24gdG8gb3ZlcmNvbWUgdGhpcyBwcm9ibGVtIGJ5IGFkZGluZyBleHRyYSB6ZXJvLXZhbHVlZCBwaXhlbHMgYXJvdW5kIHRoZSBpbnB1dCBzbyB0aGF0IHRoZSBlZmZlY3RpdmUgaW5wdXQgZGltZW5zaW9uIGlzIGhpZ2hlciwgYW5kIHRoZSBkZXNpcmVkIG91dHB1dCBkaW1lbnNpb24gaXMgb2J0YWluZWQuCgotIEluIHBhcnRpY3VsYXIsIGByIGNvbG9yaXplKCJwYWRkaW5nIiwicmVkIilgIGlzIHBhcmFtZXRlcml6ZWQgYnkgYSBwYWlyIG9mIG5vbi1uZWdhdGl2ZSBpbnRlZ2VycyAkKHBfaCwgcF92KSQsIHdoZXJlICRwX2gkIGFuZCAkcF92JCByZXNwZWN0aXZlbHkgZGVub3RlIHRoZSBudW1iZXIgb2YgYWxsLXplcm8gcm93cyBhbmQgYWxsLXplcm8gY29sdW1ucyBhZGRlZCB0byB0aGUgaW5wdXQgbWF0cml4LiBBbiBleGFtcGxlIG9mIHBhZGRpbmcgaXMgaWxsdXN0cmF0ZWQgaW4gdGhlIGZvbGxvd2luZyBmaWd1cmUuCgoKPGNlbnRlcj4KPGZpZ3VyZT4KCjxpbWcgc3JjPSJpbWFnZXMtY25uL1BhZGRpbmcucG5nIiBzdHlsZT0id2lkdGg6NzAwcHgiLz4KCjxmaWdjYXB0aW9uPklsbHVzdHJhdGlvbiBvZiBjb252b2x1dGlvbiB3aXRoIHBhZGRpbmcuPC9maWdjYXB0aW9uPgoKPC9maWd1cmU+Cgo8L2NlbnRlcj4KClwKXAoKOjo6IGJsdWUKCioqT3V0cHV0IGRpbWVuc2lvbiBhZnRlciBwYWRkaW5nOioqCgotICAgQWdhaW4gc3VwcG9zZSB0aGF0IHRoZSBpbnB1dCBkaW1lbnNpb24gaXMgJE1ee1swXX1faFx0aW1lcyBNXntbMF19X3YkIGFuZCB0aGUga2VybmVsIGRpbWVuc2lvbiBpcyAkSyBfaCBcdGltZXMgSyBfdiQuIEZ1cnRoZXIsIHN1cHBvc2UgZWFjaCBpbnB1dCBpbWFnZSBpcyBtb2RpZmllZCBieSBhZGRpbmcgJHBfaCQgcm93cyByb3VnaGx5IGhhbGYgb24gdGhlIHRvcCBhbmQgaGFsZiBvbiB0aGUgYm90dG9tLCBhbmQgJHBfdiQgY29sdW1ucyByb3VnaGx5IGhhbGYgb24gdGhlIGxlZnQgYW5kIGhhbGYgb24gdGhlIHJpZ2h0LgoKLSAgIFRoZW4gaXQgaXMgZWFzeSB0byBjaGVjayB0aGF0IHRoZSBvdXRwdXQgZGltZW5zaW9uIGlzCgpgYGB7PXRleH0KXGJlZ2lue2VxdWF0aW9ufQpcbGFiZWx7ZXE6ZGltLXdpdGgtcGFkZGluZ30KTV57WzFdfV9oIFx0aW1lcyBNXntbMV19X3YgPSAoTV57WzBdfV9oIC0gSyBfaCArIHBfaCArIDEpIFx0aW1lcyAoTV57WzBdfV92IC0gSyBfdiArIHBfdiArIDEpLiAKXGVuZHtlcXVhdGlvbn0KYGBgCgo6OjoKClwKCjo6OiBibHVlCgoqKk5vdGU6KioKCi0gICBUaGUgc2V0dGluZyAkKHBfaCwgcF92KSA9IChLX2gtMSwgS192LTEpJCBpcyBhIG1lY2hhbmlzbSBmb3IgZW5zdXJpbmcgdGhhdCB0aGUgaW5wdXQgYW5kIHRoZSBvdXRwdXQgYXJlIG9mIHRoZSBzYW1lIGRpbWVuc2lvbi4KCi0gICBBbHNvIG5vdGUgdGhhdCB0eXBpY2FsbHkgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MgYXJlIGRlc2lnbmVkIHRvIGhhdmUga2VybmVscyBvZiBvZGQgaGVpZ2h0IGFuZCBvZGQgd2lkdGguIEhlbmNlIGl0IGlzIGNvbW1vbiB0byBwYWQgd2l0aCBleGFjdGx5ICRwX2gvMiQgcm93cyBvZiB6ZXJvcyBvbiB0aGUgbGVmdCBhbmQgJHBfaC8yJCByb3dzIG9mIHplcm9zIG9uIHRoZSByaWdodCwgYW5kIHNpbWlsYXJseSBmb3IgdGhlIHZlcnRpY2FsIGRpbWVuc2lvbi4gVGhpcyBoZWxwcyBtYWludGFpbiBzcGF0aWFsIHN5bW1ldHJ5IHdoaWxlIGNvbmR1Y3RpbmcgY29udm9sdXRpb25zLgoKOjo6CgoKCiMjIyMgU3RyaWRlCgoKIC0gVGhlIGNvbnZvbHV0aW9ucyB3ZSBwcmVzZW50ZWQgdXAgdG8gbm93IGludm9sdmVkIHNoaWZ0cyBvZiB0aGUgY29udm9sdXRpb24ga2VybmVsIGJ5IG9uZSBwaXhlbCBhdCBhIHRpbWUuIFRoaXMgaXMgY2FsbGVkIGEgY29udm9sdXRpb24gd2l0aCBhIGByIGNvbG9yaXplKCJzdHJpZGUgb2Ygb25lIiwicmVkIilgLgogCiAtIEhvd2V2ZXIsIGluIG1hbnkgYXBwbGljYXRpb25zLCB3ZSBtYXkgd2lzaCB0byBzbGlkZSB0aGUgY29udm9sdXRpb24ga2VybmVsIHdpdGggYmlnZ2VyIHN0ZXBzIGluIG9yZGVyIHRvIGVpdGhlciBgciBjb2xvcml6ZSgicmVkdWNlIiwiYmx1ZSIpYCB0aGUgY29tcHV0YXRpb25hbCBjb3N0LCBvciB0byBgciBjb2xvcml6ZSgicmVkdWNlIiwiYmx1ZSIpYCB0aGUgZGltZW5zaW9uIG9mIHRoZSBvdXRwdXQgb2YgdGhlIGNvbnZvbHV0aW9uIGxheWVyLgogCiAtIEluIHBhcnRpY3VsYXIsIHdlIGNhbiBjb25kdWN0IHN0cmlkZSB3aXRoIHBvc2l0aXZlIGludGVnZXIgdmFsdWVkIHBhcmFtZXRlcnMgICQoc19oLCBzX3YpJCwgd2hlcmUgJHNfaCBcZ2VxIDEkIGFuZCAkc192IFxnZXEgMSQgcmVzcGVjdGl2ZWx5IGRlbm90ZSB0aGUgc3RyaWRlIHN0ZXAgc2l6ZXMgYWxvbmcgaGVpZ2h0IGFuZCB3aWR0aC4gIEFuIGlsbHVzdHJhdGlvbiBpcyBwcm92aWRlZCBpbiB0aGUgZm9sbG93aW5nIGZpZ3VyZS4KCgo8Y2VudGVyPgoKPGZpZ3VyZT4KCjxpbWcgc3JjPSJpbWFnZXMtY25uL1N0cmlkZS5wbmciIHN0eWxlPSJ3aWR0aDo3MDBweCIvPgoKPGZpZ2NhcHRpb24+SWxsdXN0cmF0aW9uIG9mIGNvbnZvbHV0aW9uIHdpdGggc3RyaWRlLjwvZmlnY2FwdGlvbj4KCjwvZmlndXJlPgoKPC9jZW50ZXI+CgpcCgoKOjo6IGJsdWUKCioqT3V0cHV0IGRpbWVuc2lvbiBhZnRlciBwYWRkaW5nIGFuZCBzdHJpZGU6KioKCldpdGggYm90aCBwYWRkaW5nIG9mICQocF9oLCBwX3YpJCBhbmQgc3RyaWRlIG9mICQoc19oLCBzX3YpJCwgdGhlIG91dHB1dCBkaW1lbnNpb24gd2lsbCBiZQoKXGJlZ2lue2FsaWdufQpcbGFiZWx7ZXFuOk91dHB1dF9zaGFwZV9hZnRlcl9zdHJpZGV9Ck1ee1sxXX1faCBcdGltZXMgTV57WzFdfV92ID0gXGxlZnQoMSArIFxCaWdcbGZsb29yIFxmcmFje01ee1swXX1faCAtIEsgX2ggKyBwX2h9e3NfaH0gXEJpZ1xyZmxvb3IgXHJpZ2h0KSBcdGltZXMgXGxlZnQoMSArIFxCaWdcbGZsb29yIFxmcmFje01ee1swXX1fdiAtIEsgX3YrIHBfdn17c192fSBcQmlnXHJmbG9vclxyaWdodCkuClxlbmR7YWxpZ259Cgo6Ojo6CgoKIyMjIyBEaWxhdGlvbgoKIC0gYHIgY29sb3JpemUoIkRpbGF0YXRpb24iLCJyZWQiKWAgaXMgYSB0ZWNobmlxdWUgZm9yIGluY3JlYXNpbmcgdGhlIGByIGNvbG9yaXplKCJyZWNlcHRpdmUgZmllbGQiLCJyZWQiKWAgb2YgYSBmaWx0ZXIgd2l0aG91dCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2YgbGVhcm5lZCBwYXJhbWV0ZXJzLiAKIAogLSBUaGlzIGlzIGFjaGlldmVkIGJ5IGByIGNvbG9yaXplKCJzcHJlYWRpbmcgb3V0IiwicmVkIilgIHRoZSBlbGVtZW50cyBvZiB0aGUga2VybmVsIG1hdHJpeCAkVyQgdmlhIHRoZSBpbnNlcnRpb24gb2YgemVyb3MgYmV0d2VlbiBlbGVtZW50cy4gVGhpcyBhbHRlcmF0aW9uIGFsbG93cyB0aGUga2VybmVsIHRvIGNvdmVyIGEgbGFyZ2VyIGFyZWEgb2YgdGhlIGlucHV0IGltYWdlLCB0aGF0IGlzLCB0aGUgZWZmZWN0aXZlIGtlcm5lbCBkaW1lbnNpb24gaW5jcmVhc2VzLiAKIAogLSBUaGUgbGV2ZWwgb2YgZGlsYXRpb24gKG9yLCB0aGUgbnVtYmVyIG9mIHplcm9zIGFkZGVkKSBpcyBjb250cm9sbGVkIHVzaW5nIGEgcGFpciBvZiB0d28gcG9zaXRpdmUgaW50ZWdlcnMgJChkX2gsIGRfdikkLiBJbiBwYXJ0aWN1bGFyLCBpdCBjb252ZXJ0cyBhIGtlcm5lbCBvZiBzaXplICRLX2ggXHRpbWVzIEtfdiQgdG8gYSBrZXJuZWwgb2Ygc2l6ZSAkS19oJyBcdGltZXMgS192JyA9IChkX2goS19oIC0gMSkgKyAxKSBcdGltZXMgKGRfdihLX3YgLSAxKSArIDEpJCBieSBgciBjb2xvcml6ZSgiYWRkaW5nIGFsbC16ZXJvIGNvbHVtbnMgYW5kIGFsbC16ZXJvIHJvd3MgYmV0d2VlbiB0aGUgY29sdW1ucyBhbmQgcm93cyBvZiB0aGUgb3JpZ2luYWwga2VybmVsIiwiYmx1ZSIpYC4KIAogLSBTZWUgdGhlIGZvbGxvd2luZyBmaWd1cmUgZm9yIGlsbHVzdHJhdGlvbi4KCgo8Y2VudGVyPgo8ZmlndXJlPgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9EaWxhdGlvbi5wbmciIHN0eWxlPSJ3aWR0aDo3MDBweCIvPgo8ZmlnY2FwdGlvbj5GaWd1cmUgKGEpOiBObyBkaWxhdGlvbiAoaS5lLiwgJChkX2gsIGRfdikgPSAoMSwxKSQpLiBGaWd1cmUgKGIpOiBEaWFsdGlvbiB3aXRoICQoZF9oLCBkX3YpID0gKDIsMikkLiBTb3VyY2U6IEhlIGV0IGFsLCAyMDIxLjwvZmlnY2FwdGlvbj4KPC9maWd1cmU+CjwvY2VudGVyPgoKXApcCgoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vZmlndXJlNi44LWRpbGF0aW9uLnBuZyIgc3R5bGU9IndpZHRoOjcwMHB4Ii8+CjxmaWdjYXB0aW9uPiBGaWd1cmU6IElsbHVzdHJhdGlvbiBvZiBkaWxhdGlvbiBvcGVyYXRpb24gd2l0aCAkKGRfaCwgZF92KSA9ICgyLDIpJCBleHRlbmRpbmcgYSAkM1x0aW1lcyAzJCBmaWx0ZXIgdG8gY3JlYXRlIGEgcmVjZXB0aXZlIGZpZWxkIG9mICQ1XHRpbWVzIDUkIGNoYW5uZWxzLjwvZmlnY2FwdGlvbj4KPC9maWd1cmU+CjwvY2VudGVyPgoKXApcCgoKCgo6OjogYmx1ZQoKKipPdXRwdXQgZGltZW5zaW9uIGFmdGVyIHBhZGRpbmcsIHN0cmlkZSBhbmQgZGlsYXRpb246KioKCi0gICBUaGUgdGhyZWUgYWx0ZXJhdGlvbnMsIG5hbWVseSBwYWRkaW5nIHdpdGggJChwX2gsIHBfdikkLCBzdHJpZGUgd2l0aCAkKHNfaCwgc192KSQgYW5kIGRpbGF0aW9uIHdpdGggJChkX2gsIGRfdikkLCByZXN1bHRzIGluIGFuIG91dHB1dCBvZiBkaW1lbnNpb24gCgpcYmVnaW57ZXF1YXRpb259Ck1faF57WzFdfSBcdGltZXMgTV92XntbMV19IAo9IAogXGxlZnQoMSArIFxCaWdcbGZsb29yIFxmcmFje01faF57WzBdfSAtIGRfaChLX2gtMSktMSArIHBfaH17c19ofSBcQmlnXHJmbG9vciBccmlnaHQpIFx0aW1lcyBcbGVmdCgxICsgXEJpZ1xsZmxvb3IgXGZyYWN7TV92XntbMF19IC0gZF92KEtfdi0xKS0xICsgcF92fXtzX3Z9IFxCaWdccmZsb29yXHJpZ2h0KSwKXGVuZHtlcXVhdGlvbn0gJFxxcXVhZCR3aGVyZSAkXGxmbG9vciB1IFxyZmxvb3IkIHJlcHJlc2VudHMgdGhlIGxhcmdlc3QgaW50ZWdlciBub3QgZ3JlYXRlciB0aGFuICR1JC4KCjo6OgoKCiMjIyBJbnB1dHMgd2l0aCBNdWx0aXBsZSBDaGFubmVscwoKLSBTbyBmYXIgaW4gdGhpcyBzZWN0aW9uIHdlIGhhdmUgbG9va2VkIGF0IHRoZSBjYXNlIHdoZXJlIGVhY2ggaW5wdXQgaXMgYSBtYXRyaXgsIHVzdWFsbHkgcmVwcmVzZW50aW5nIGEgYHIgY29sb3JpemUoImdyZXkgc2NhbGUgaW1hZ2UiLCJibGFjayIpYC4gCgotIEhvd2V2ZXIsIGNvbnZvbHV0aW9uYWwgbmV0d29ya3Mgb2Z0ZW4gZGVhbCB3aXRoIGlucHV0cyBjb21wcmlzZWQgb2YgbXVsdGlwbGUgKmNoYW5uZWxzKi4gRm9yIGluc3RhbmNlLCBgciBjb2xvcml6ZSgiYSBjb2xvciBpbWFnZSBoYXMgdGhyZWUgY2hhbm5lbHMiLCJyZWQiKWAgcmVwcmVzZW50aW5nIHRoZSByZWQsIGdyZWVuLCBhbmQgYmx1ZSBjb21wb25lbnRzLiAKCi0gV2hlbiB3ZSBoYXZlIHN1Y2ggZGF0YSB3aXRoIG11bHRpcGxlIGNoYW5uZWxzLCBpbnB1dCB0byBhIGNvbnZvbHV0aW9uIGxheWVyIGlzIG5vIGxvbmdlciBhIG1hdHJpeCBidXQgaXMgcmF0aGVyIHJlcHJlc2VudGVkIGFzIGEgdGhyZWUgZGltZW5zaW9uYWwgdGVuc29yIG9mIGRpbWVuc2lvbiwgc2F5LCAkTV57WzBdfV9jIFx0aW1lcyBNXntbMF19X2ggXHRpbWVzIE1ee1swXX1fdiQsIHdoZXJlIHRoZSBgciBjb2xvcml6ZSgiZGVwdGgiLCJyZWQiKWAgJE1ee1swXX1fYyQgZGVub3RlcyB0aGUgbnVtYmVyIG9mIGNoYW5uZWxzLCBhbmQgdGhlIG90aGVyIHR3byBudW1iZXJzIGFyZSBmb3IgdGhlIGhvcml6b250YWwgYW5kIHZlcnRpY2FsIGRpbWVuc2lvbnMgYXMgdXNlZCBwcmV2aW91c2x5LiAKCi0gSGVuY2UgZm9yIGNvbG9yIGltYWdlcyB3ZSB1c2UgJE1ee1swXX1fYyA9IDMkIGFuZCBmdXJ0aGVyLCBhcyB3ZSBkZXNjcmliZSBpbiB0aGUgc2VxdWVsLCBmb3IgYSBoaWRkZW4gbGF5ZXIgYHIgY29sb3JpemUoIndlIG9mdGVuIGhhdmUgbW9yZSB0aGFuICQzJCBpbnB1dCBjaGFubmVscyIsInJlZCIpYCB0byB0aGUgbGF5ZXIuCgotIFRvIGRlYWwgd2l0aCBpbnB1dHMgd2l0aCBtdWx0aXBsZSBjaGFubmVscywgd2UgdXNlIGEga2VybmVsICRXJCB3aXRoIHRoZSBgciBjb2xvcml6ZSgic2FtZSBkZXB0aCBhcyB0aGUgaW5wdXQiLCJibHVlIilgLiBUaGF0IGlzLCB3ZSB1c2UgYSBrZXJuZWwgb2YgZGltZW5zaW9uICRLX2MgXHRpbWVzIEtfaCBcdGltZXMgS192JCBzdWNoIHRoYXQgJEtfYyA9IE1ee1swXX1fYyQsICRLX2ggXGxlcSBNXntbMF19X2gkLCBhbmQgJEtfdiBcbGVxIE1ee1swXX1fdiQuIAoKCjxjZW50ZXI+CjxmaWd1cmU+CjxpbWcgc3JjPSJpbWFnZXMtY25uL2ZpZ3VyZS02LjEwLnBuZyIgc3R5bGU9IndpZHRoOjcwMHB4Ii8+CjxmaWdjYXB0aW9uPklsbHVzdHJhdGlvbiBvZiBjb252b2x1dGlvbiB3aGVuIHRoZSBpbnB1dCBoYXMgbXVsdGlwbGUgY2hhbm5lbHMuPC9maWdjYXB0aW9uPgo8L2ZpZ3VyZT4KPC9jZW50ZXI+CgpcClwKCgoKCiMjIyBPdXRwdXRzIHdpdGggTXVsdGlwbGUgQ2hhbm5lbHMKCi0gVW50aWwgbm93LCByZWdhcmRsZXNzIG9mIHRoZSBudW1iZXIgb2YgaW5wdXQgY2hhbm5lbHMsIHRoZSBvdXRwdXQgb2YgYSBjb252b2x1dGlvbiBsYXllciBpcyBhIGByIGNvbG9yaXplKCJtYXRyaXgiLCJyZWQiKWAsIGRlbm90ZWQgdmlhICRhXntbMV19JC4gVGhpcyBpcyBiZWNhdXNlLCBzbyBmYXIgdGhlcmUgaXMgb25seSBgciBjb2xvcml6ZSgib25lIGtlcm5lbCIsInJlZCIpYCwgcG9zc2libHkgYSB0ZW5zb3IsIG9wZXJhdGluZyBvbiB0aGUgaW5wdXQgdG8gdGhlIGNvbnZvbHV0aW9uIGxheWVyLiAKCi0gSG93ZXZlciwgbW9zdCBwb3B1bGFyIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzIGhhdmUgY29udm9sdXRpb25hbCBsYXllcnMgd2l0aCBgciBjb2xvcml6ZSgibXVsdGlwbGUga2VybmVscyIsInJlZCIpYCBvcGVyYXRpbmcgb24gdGhlIGlucHV0IHNpbXVsdGFuZW91c2x5LiBJbiB0aGlzIGNhc2UsIHRoZSBvdXRwdXQgb2YgdGhlIGxheWVyIGlzIGEgY29sbGVjdGlvbiBvZiBtYXRyaWNlcyBkZW5vdGVkIGJ5ICRhXntbMSxqXX0kIGZvciAkaj0xLCBcbGRvdHMsIE1fY157WzFdfSQsIHdoZXJlICRNX2Nee1sxXX0kIGlzIHRoZSBgciBjb2xvcml6ZSgibnVtYmVyIG9mIG91dHB1dCBjaGFubmVscyIsInJlZCIpYC4gQ29uc2VxdWVudGx5LCB0aGUgb3V0cHV0IGNhbiBiZSB2aWV3ZWQgYXMgYSAzLWRpbWVuc2lvbmFsIHRlbnNvciBvZiBkaW1lbnNpb24gJE1ee1sxXX1fYyBcdGltZXMgTV57WzFdfV9oIFx0aW1lcyBNXntbMV19X3YkLgoKLSBUaGUgZm9sbG93aW5nIGZpZ3VyZSBpbGx1c3RyYXRlIHRoaXMuCgo8Y2VudGVyPgo8ZmlndXJlPgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9maWd1cmUtNi4xMS5wbmciIHN0eWxlPSJ3aWR0aDo3MDBweCIvPgo8ZmlnY2FwdGlvbj5JbGx1c3RyYXRpb24gb2YgY29udm9sdXRpb24gd2hlbiBtdWx0aXBsZSBrZXJuZWxzIGFyZSB1c2VkIGluIHBhcmFsbGVsIChpLmUuLCBtdWx0aXBsZSBvdXRwdXQgY2hhbm5lbHMpLjwvZmlnY2FwdGlvbj4KPC9maWd1cmU+CjwvY2VudGVyPgoKXApcCgojIEJ1aWxkaW5nIGEgQ29udm9sdXRpb25hbCBOZXVyYWwgTmV0d29yawoKLSBXZSBoYXZlIG5vdyBhY3F1aXJlZCBhbGwgdGhlIGNydWNpYWwgZWxlbWVudHMgbmVjZXNzYXJ5IGZvciBjb25zdHJ1Y3RpbmcgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MsIHN1Y2ggYXMgdGhlIFZHRzE5IG1vZGVsIGRlcGljdGVkIGVhcmxpZXIuIFdlIG5vdyBwdXQgdGhlIHBpZWNlcyB0b2dldGhlciBmb3IgY29uc3RydWN0aW5nIGEgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29yayB0aGF0LCBpbiBhZGRpdGlvbiB0byBjb252b2x1dGlvbmFsIGxheWVycywgaW5jbHVkZXMgZnVsbHkgY29ubmVjdGVkIGxheWVycywgYW5kIGByIGNvbG9yaXplKCJwb29saW5nIiwicmVkIilgLiBsYXllcnMuIAoKLSBBIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmsgaXMgZ2VuZXJhbGx5ICoqZGVlcCoqIHdpdGggbXVsdGlwbGUgbGF5ZXJzLCBzaW1pbGFyIHRvIGZlZWQtZm9yd2FyZCBuZXR3b3Jrcy4gSG93ZXZlciwgdW5saWtlIGZlZWQtZm9yd2FyZCBuZXR3b3JrcyB3aGljaCBjb25zaXN0IG9mIG9ubHkgZnVsbHkgY29ubmVjdGVkIGxheWVycywgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29ya3MgaGF2ZSBkaWZmZXJlbnQgdHlwZXMgb2YgbGF5ZXJzLCBvZiB3aGljaCBzb21lIGFyZSBgciBjb2xvcml6ZSgidHJhaW5hYmxlIiwicmVkIilgIGFuZCB0aGUgb3RoZXJzIGFyZSAqKm5vdCoqLCBhbmQgdGhlIHRyYWluYWJsZSBsYXllcnMgYXJlIGZ1cnRoZXIgYnJva2VuIHVwIGludG8gYHIgY29sb3JpemUoImNvbnZvbHV0aW9uYWwgbGF5ZXJzIiwicmVkIilgIGFuZCBgciBjb2xvcml6ZSgiZGVuc2UgbGF5ZXJzIiwiYmx1ZSIpYC4gCgotIElmIHdlIHVzZSAkTCQgZm9yIHRoZSBudW1iZXIgb2YgbGF5ZXJzLCB0aGVuCgpcWwpMID0gTF97XHRleHR7dHJhaW59fSArIExfe1x0ZXh0e3Bvb2x9fSwKXHFxdWFkClx0ZXh0e3doZXJlfQpccXF1YWQKTF97XHRleHR7dHJhaW59fSA9IExfe1x0ZXh0e2NvbnZ9fSArIExfe1x0ZXh0e2RlbnNlfX0uClxdCgotIEhlcmUgJExfe1x0ZXh0e3RyYWlufX0kLCBjb3VudHMgdGhlIG51bWJlciBvZiB0cmFpbmFibGUgbGF5ZXJzLCB3aGVyZWFzICRMX3tcdGV4dHtwb29sfX0kIGNvdW50cyB0aGUgbnVtYmVyIG9mIGxheWVycyB0aGF0IGRvIG5vdCBoYXZlIHRyYWluYWJsZSBwYXJhbWV0ZXJzLiBGdXJ0aGVyLCB0aGUgdHJhaW5hYmxlIGxheWVycyBhcmUgZWl0aGVyIGNvbnZvbHV0aW9uYWwgbGF5ZXJzLCBjb3VudGVkIGJ5ICRMX3tcdGV4dHtjb252fX0kLCBvciBmdWxseSBjb25uZWN0ZWQgbGF5ZXJzLCBjb3VudGVkIGJ5ICRMX3tcdGV4dHtkZW5zZX19JC4gCgotIEZvciBleGFtcGxlLCBpbiB0aGUgVkdHMTkgbmV0d29yaywKClxiZWdpbntlcXVhdGlvbn0KXGxhYmVse2VxOmNvdW50aW5nLWNvbnYtbGF5ZXJzfQpMID0gMjQsClxxcXVhZApMX3tcdGV4dHt0cmFpbn19ID0gMTksClxxcXVhZApMX3tcdGV4dHtwb29sfX0gPSA1LApccXF1YWQKTF97XHRleHR7Y29udn19ID0gMTYsClxxcXVhZApMX3tcdGV4dHtkZW5zZX19ID0gMywKXGVuZHtlcXVhdGlvbn0KCi0gU2VlIHRoZSBmb2xsb3dpbmcgZmlndXJlIHRvIHZlcmlmeSB0aGVzZSBudW1iZXJzLgoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vVkdHMTkucG5nIiBzdHlsZT0id2lkdGg6NzAwcHg7Ii8+CjwvZmlndXJlPgo8L2NlbnRlcj4KCi0gU2ltaWxhciB0byBhIGZlZWQtZm9yd2FyZCBuZXR3b3JrLCB0aGUgZ29hbCBvZiBhIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmsgaXMgdG8gYHIgY29sb3JpemUoImFwcHJveGltYXRlIiwicmVkIilgICBzb21lIHVua25vd24gZnVuY3Rpb24gJGZeKihcY2RvdCkkLiBGb3IgaW5zdGFuY2UsIGZvciBjbGFzc2lmaWNhdGlvbiBvZiBpbWFnZSBkYXRhIHdpdGggYW5pbWFsIGZhY2VzLCB0aGUgZnVuY3Rpb24gdmFsdWUgJGZeKih4KSQgZm9yIGFueSBnaXZlbiBpbWFnZSAkeCQgbWF5IAp5aWVsZCBhIGByIGNvbG9yaXplKCJwcm9iYWJpbGl0eSB2ZWN0b3IiLCJyZWQiKWAgd2l0aCB0aGUgaGlnaGVzdCB3ZWlnaHQgb24gdGhlIGluZGV4IGFzc29jaWF0ZWQgd2l0aCB0aGUgbGFiZWwgb2YgdGhlIGltYWdlICR4JC4gCgoKOjo6IGJsdWUKLSBBIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmsgZGVmaW5lcyBhIG1hcHBpbmcgJGZfe1xtYXRoYmJ7XHRoZXRhfX0oXGNkb3QpJCBhbmQgYHIgY29sb3JpemUoImxlYXJucyB0aGUgdmFsdWVzIG9mIHRoZSB1bmtub3duIHBhcmFtZXRlcnMiLCJyZWQiKWAgJFxtYXRoYmJ7XHRoZXRhfSQgdGhhdCBpZGVhbGx5IHJlc3VsdCBpbiAkZl4qKHgpIFxhcHByb3ggZl9cdGhldGEoeCkkIGZvciBhcyBtYW55IGlucHV0IGltYWdlcyAkeCQgYXMgcG9zc2libGUuIAo6OjogCgotIEluIGdlbmVyYWwsIHNpbWlsYXIgdG8gZmVlZC1mb3J3YXJkIG5ldHdvcmtzLCB0aGUgYXBwcm94aW1hdGluZyBmdW5jdGlvbiAkZl9cdGhldGEoXGNkb3QpJCBpcyByZWN1cnNpdmVseSBjb21wb3NlZCBhcwoKXFsKZl97XG1hdGhiZntcdGhldGF9fSh4KT1mX3tcbWF0aGJie1x0aGV0YX1ee1tMXX19XntbTF19KGZfe1xtYXRoYmJ7XHRoZXRhfV57W0wtMV19fV57W0wtMV19KFxsZG90cyAoZl97XG1hdGhiYntcdGhldGF9XntbMV19fV57WzFdfSh4KSlcbGRvdHMpKSwKXF0KJFxxcXVhZCR3aGVyZSBmb3IgZWFjaCAkXGVsbCQsIHRoZSBmdW5jdGlvbiAkZl97e1xtYXRoYmJ7XHRoZXRhfX1ee1tcZWxsXX19XnsoXGVsbCl9KFxjZG90KSQgaXMgYXNzb2NpYXRlZCB3aXRoIHRoZSAkXGVsbCR0aCBsYXllciB3aGljaCBkZXBlbmRzIG9uIHRoZSBsYXllcidzIHBhcmFtZXRlcnMgJHtcbWF0aGJie1x0aGV0YX19XntbXGVsbF19IFxpbiBcVGhldGFee1tcZWxsXX0kLiAKCjo6OiBibHVlCi0gTm90ZSB0aGF0IGZvciBsYXllcnMgdGhhdCBhcmUgYHIgY29sb3JpemUoIm5vdCB0cmFpbmFibGUiLCJyZWQiKWAgKGFzIGNvdW50ZWQgdmlhICRMX3tcdGV4dHtwb29sfX0kKSwgdGhlIHBhcmFtZXRlciBzcGFjZSAkXFRoZXRhXntbXGVsbF19JCBpcyBlbXB0eS4gCjo6OgoKLSBTaW1pbGFybHkgdG8gZmVlZC1mb3J3YXJkIG5ldHdvcmtzLCBpdCBpcyB1c2VmdWwgdG8gZGVub3RlIHRoZSBuZXVyb24gYWN0aXZhdGlvbnMgb2YgdGhlIG5ldHdvcmsgdmlhICRhXntbMV19LGFee1syXX0gXGxkb3RzLCBhXntbTF19JCB3aGVyZSAkYV57W0xdfSA9IFxoYXR7eX0kIGlzIHRoZSBvdXRwdXQsIGFuZCBmb3IgJFxlbGw9MSxcbGRvdHMsTC0xJCwKXFsKYV57W1xlbGxdfSA9IGZee1tcZWxsXX1fe1x0aGV0YV57W1xlbGxdfX0oYV57W1xlbGwtMV19KSwKXF0Kd2l0aCAkYV57WzBdfSA9IHgkLiAKCgojIyMgQ29udm9sdXRpb25hbCBMYXllcnMKCi0gV2hlbiB0aGUgJFxlbGwkLXRoIGxheWVyIG9mIHRoZSBuZXR3b3JrIGlzIGEgY29udm9sdXRpb24gbGF5ZXIsIHRoZW4gJGZfe3tcbWF0aGJie1x0aGV0YX19XntbXGVsbF19fV57KFxlbGwpfShcY2RvdCkkIHRha2UgdGhlIG91dHB1dCAkYV57W1xlbGwtMV19JCBvZiB0aGUgcHJldmlvdXMgbGF5ZXIgYXMgdGhlIGlucHV0IGFuZCBjb25kdWN0cyBgciBjb2xvcml6ZSgiY29udm9sdXRpb24iLCJyZWQiKWAuCgotIEluIHRoaXMgY2FzZSB0aGUgaW5wdXQgYW5kIG91dHB1dCBhcmUgZ2VuZXJhbGx5IGByIGNvbG9yaXplKCIzLWRpbWVuc2lvbmFsIHRlbnNvcnMiLCJyZWQiKWAgYXMgd2UgaGF2ZSBzZWVuIGluIHRoZSBwcmV2aW91c2x5LgoKCiMjIyBQb29saW5nIExheWVycwoKCi0gQXMgbWVudGlvbmVkIGFib3ZlLCB0aGVyZSBhcmUgYWxzbyBgciBjb2xvcml6ZSgibm9uLXRyYWluYWJsZSBsYXllcnMiLCJyZWQiKWAgY291bnRlZCBieSAkTF97XHRleHR7cG9vbH19JCBhbmQgdGhlc2UgYXJlIHR5cGljYWxseSBjYWxsZWQgYHIgY29sb3JpemUoInBvb2xpbmcgbGF5ZXJzIiwicmVkIilgLiBUaGUgbWFpbiBpZGVhIG9mIGEgIGByIGNvbG9yaXplKCJwb29saW5nIGxheWVyIiwicmVkIilgIGlzIHRvIHJlZHVjZSB0aGUgaGVpZ2h0IGFuZCB3aWR0aCBvZiB0aGUgaW5wdXQgdGVuc29yICRhXntbXGVsbC0xXX0kIHRvIGFjaGlldmUgYSBsb3dlciBkaW1lbnNpb25hbCBvdXRwdXQgdGVuc29yICRhXntbXGVsbF19JCB3aXRob3V0IGNoYW5naW5nIHRoZSBkZXB0aC4gCgotIEdlbmVyYWxseSBmb3Igc29tZSBmaXhlZCBjaGFubmVsICRqJCwgYW5kIHBpeGVsIGNvb3JkaW5hdGVzIG9mIHRoZSBvdXRwdXQgJChpLGspJCwgYSBwb29saW5nIG9wZXJhdGlvbiBvcGVyYXRlcyBvbiBwaXhlbHMgZnJvbSBhIHdpbmRvdyBpbiB0aGUgaW5wdXQgZGVub3RlZCB2aWEgJFxtYXRoc2Nye0l9X3soaSxrKX0kLiBIZXJlICRcbWF0aHNjcntJfV97KGksayl9JCBpcyBhIHNldCBvZiBwaXhlbCBjb29yZGluYXRlcyBpbiB0aGUgaW5wdXQgdGhhdCBhcmUgbWFwZWQgdG8gdGhlIHNwZWNpZmljIG91dHB1dCBwaXhlbCAkKGksaykkLiBUaGVyZSBhcmUgdHdvIHBvcHVsYXIgcG9vbGluZyB0ZWNobmlxdWVzIHVzZWQgaW4gcHJhY3RpY2UsIG5hbWVseSwgYHIgY29sb3JpemUoIm1heC1wb29saW5nIiwicmVkIilgIGFuZCBgciBjb2xvcml6ZSgiYXZlcmFnZS1wb29saW5nIiwicmVkIilgLiAKCi0gRm9yIGVhY2ggY2hhbm5lbCAkaiQsIHRoZSBwb29saW5nIG9wZXJhdGlvbiBjYW4gYmUgc3VtbWFyaXplZCBhcywKClxbClxCaWdbYV57W1xlbGxdfV97KGopfVxCaWddX3tpLGt9ID0KXGJlZ2lue2Nhc2VzfQogXG1heF97KGknLGsnKSBcaW4gXG1hdGhzY3J7SX1feyhpLGspfX0gflxCaWdbYV57W1xlbGwtMV19X3soail9IFxCaWddX3tpJyxrJ30sICYgXHFxdWFkIFx0ZXh0eyhtYXgtcG9vbGluZyl9IFxcWzE1cHRdCiAgXGZyYWN7MX17fFxtYXRoc2Nye0l9X3soaSxrKX18fSAgXHN1bV97KGknLGsnKSBcaW4gXG1hdGhzY3J7SX1feyhpLGspfX0gflxCaWdbYV57W1xlbGwtMV19X3soail9IFxCaWddX3tpJyxrJ30uICAmIFxxcXVhZCBcdGV4dHsoYXZlcmFnZS1wb29saW5nKX0gCiBcZW5ke2Nhc2VzfQpcXQoKLSBBcyBpcyBldmlkZW50LCBtYXgtcG9vbGluZyB0YWtlcyB0aGUgbWF4aW1hbCBwaXhlbCB2YWx1ZSB3aXRoaW4gdGhlIHdpbmRvdyBhcyB0aGUgb3V0cHV0LCB3aGlsZSBhdmVyYWdlIHBvb2xpbmcgYXZlcmFnZXMgcGl4ZWwgdmFsdWVzIHdpdGhpbiB0aGUgd2luZG93IGZvciB0aGUgb3V0cHV0LiBTZWUgdGhlIGZvbGxvd2luZyBmaWd1cmUgZm9yIGFuIGlsbHVzdHJhdGlvbi4KCgo8Y2VudGVyPgo8ZmlndXJlPgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9wb29saW5nX2Jvb2sucG5nIiBzdHlsZT0id2lkdGg6NzAwcHgiLz4KPC9maWd1cmU+CjwvY2VudGVyPgoKXApcCgo6OjogYmx1ZQoKKipOb3RlOioqIFRoZSBpZGVhIG9mIHBvb2xpbmcgaW50ZXJwbGF5cyB3aXRoIHRoZSBub3Rpb24gdGhhdCB0aGUgKippbml0aWFsIGxheWVycyoqIG9mIGEgY29udm9sdXRpb25hbCBuZXR3b3JrIGZvY3VzIG9uICoqcGl4ZWwgbGV2ZWwgZmVhdHVyZXMqKiBzaW1pbGFyIHRvIGVkZ2UgZGV0ZWN0aW9uLCBhbmQgYXMgd2UgcHJvZ3Jlc3MgdG93YXJkcyB0aGUgZmluYWwgbGF5ZXJzIG9mIHRoZSBuZXR3b3JrLCB0aGUgaW5mb3JtYXRpb24gaXMgYWdncmVnYXRlZCB0byBhZGRyZXNzICoqZ2VuZXJhbCBxdWVzdGlvbnMqKiBhYm91dCB0aGUgd2hvbGUgaW1hZ2UuIFRodXMgZGVlcGVyIGxheWVycyBhcmUgYHIgY29sb3JpemUoImxlc3Mgc2Vuc2l0aXZlIiwicmVkIilgIHRvIHRyYW5zbGF0aW9uIGNoYW5nZXMgb24gdGhlIGlucHV0IGltYWdlIGNvbXBhcmVkIHRvIHRoZSBpbml0aWFsIGxheWVycy4gRm9yIGluc3RhbmNlLCB0aGUgYW5zd2VyIHRvIGEgcXVlc3Rpb24gYGAqKmlzIHRoZXJlIGEgYmlyZCBpbiB0aGUgcGhvdG8/KionJyBpcyB0aGUgc2FtZSBpcnJlc3BlY3RpdmUgb2YgaXQgaXMgcGVsaWNhbiBvciBhIHNlYWd1bGwsIGV2ZW4gdGhvdWdoIHRoZSBjb3JyZXNwb25kaW5nIG91dHB1dHMgZnJvbSB0aGUgaW5pdGlhbCBsYXllcnMgbG9vayBkaWZmZXJlbnQuICBQb29saW5nIGxheWVycyBhcmUgYXBwbGllZCBhZnRlciBjb252b2x1dGlvbmFsIGxheWVycyB0byBzdXBwb3J0IHRoaXMgYWdncmVnYXRpb24gYnkgKipwcm9ncmVzc2l2ZWx5IHJlZHVjaW5nIHRoZSBzcGF0aWFsIGRpbWVuc2lvbnMqKiwgYWxsb3dpbmcgdGhlIG5ldHdvcmsgdG8gcmVjb2duaXplIGxhcmdlciBwYXR0ZXJucyB3aGlsZSBmb2N1c2luZyBsZXNzIG9uIHNwZWNpZmljIHBpeGVsIGxvY2F0aW9ucy4KCjo6OjoKCgojIyMgRnVsbHkgQ29ubmVjdGVkIExheWVycwoKLSBTb21lIGxheWVycyBvZiBhIENOTiBjYW4gYmUgYHIgY29sb3JpemUoImZ1bGx5IGNvbm5lY3RlZCIsInJlZCIpYC4gU3VjaCBsYXllcnMgYXJlIHR5cGljYWxseSBkZXBsb3llZCBhdCB0aGUgZW5kIG9mIHRoZSBuZXR3b3JrLiBUaGlzIGlzIGJlY2F1c2UgdGhlIHR5cGljYWwgdGFzayBvZiB0aGUgbGFzdCBsYXllcnMgb2YgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29yayBpcyB0byBhZGRyZXNzIGdlbmVyYWwgcXVlc3Rpb25zLCBzdWNoIGFzIGByIGNvbG9yaXplKCJjbGFzc2lmaWNhdGlvbiIsInJlZCIpYCBvZiB0aGUgb2JqZWN0cyBpbiB0aGUgaW1hZ2UuIAoKLSBOb3RlIHRoYXQgc2luY2UgZnVsbHkgY29ubmVjdGVkIGxheWVycyBvcGVyYXRlIG9uIHZlY3RvcnMgYXMgdGhlIGlucHV0LCBpbiBjYXNlcyB0aGUgcHJldmlvdXMgbGF5ZXIgaGFzIGEgdGVuc29yIGFzIG91dHB1dCwgaXQgaXMgYHIgY29sb3JpemUoImZsYXR0ZW5lZCB0byBhIHZlY3RvciIsInJlZCIpYC4KCgojIyMgUHJhY3RpY2UgRXhjZXJjaXNlCgo6Ojogb3JhbmdlCioqUHJhY3RpY2FsIDI6KioKCi0gICBPcGVuIFR1dG9yaWFsIDIgb2YgQ05OIGF2YWlsYWJsZSBvbiB0aGUgd29ya3Nob3AgR2l0SHViIHBhZ2UuIEFsdGVybmF0aXZlbHksIGNsaWNrIDxhIGhyZWY9Imh0dHBzOi8vY29sYWIucmVzZWFyY2guZ29vZ2xlLmNvbS9kcml2ZS8xcWdMNkVVMjRpMUVSUjJJMHFwRVRxbklHdzFmS0M3LUs/dXNwPXNoYXJpbmcidGFyZ2V0PSJfYmxhbmsiPmhlcmU8L2E+LgoKLSAgIFNhdmUgYSBjb3B5IG9mIHRoaXMgaW4geW91ciBHb29nbGUgQ29sYWIuCgotICAgSW4gdGhpcyBleGVyY2lzZSwgd2UgYnVpbGQgYSBzaW1wbGUgQ05OIGZvciBjbGFzc2lmaWNhdGlvbiB1c2luZyBDaUZBUjEwIGRhdGFzZXQuCjo6OgoKIyMjIFZHRzE5IFJldmlzaXRlZAoKLSBXZSBub3cgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBhcmNoaXRlY3R1cmUgb2Ygb3VyIHJ1bm5pbmcgZXhhbXBsZSBuZXR3b3JrLCBWR0cxOS4gCgotIFdoaWxlIHRoaXMgaXMgbm90IHRoZSBtb3N0IG1vZGVybiBjb252b2x1dGlvbmFsIGFyY2hpdGVjdHVyZSwgaXQgaXMgaW5zdHJ1Y3RpdmUgdG8gY29uc2lkZXIgaXQgaGVyZSBzaW5jZSBpdCBmYWxscyBkaXJlY3RseSB3aXRoaW4gdGhlIHBhcmFkaWdtcyBkaXNjdXNzZWQgYWJvdmUuIAoKLSBUYWJsZSBiZWxvdyBwcm92aWRlcyBjb21wbGV0ZSBkZXRhaWxzLgoKCjxjZW50ZXI+CjxmaWd1cmU+PGltZyBzcmM9ImltYWdlcy1jbm4vdGFibGUtdmdnMTkucG5nIiBzdHlsZT0id2lkdGg6NzAwcHgiLz48L2ZpZ3VyZT4KPC9jZW50ZXI+ClwKXAoKLSBUaGUgdG90YWwgbnVtYmVyIG9mIGxlYXJuZWQgcGFyYW1ldGVycyBvZiBWR0cxOSBuZXR3b3JrcyBpcyBhcm91bmQgMTM4IG1pbGxpb25zLiAKCiMjIyBEcm9wb3V0CgotICoqRHJvcG91dCoqIGlzIGFub3RoZXIgcG9wdWxhciB0ZWNobmlxdWUgZm9yIGltcHJvdmluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgQ05Ocy4gT25lIHNpbXBsZSB0aXAgZm9yIHVzaW5nIGRyb3BvdXQgaXMgdG8gaW5jbHVkZSBpdCBgciBjb2xvcml6ZSgiYWZ0ZXIgZWFjaCBmdWxseSBjb25uZWN0ZWQgbGF5ZXIiLCJyZWQiKWAgaW4gdGhlIG5ldHdvcmsuIFRoaXMgaGVscHMgdG8gcmVndWxhcml6ZSB0aGUgbmV0d29yayBhbmQgcHJldmVudCBvdmVyZml0dGluZywgd2hpY2ggY2FuIGltcHJvdmUgdGhlIGdlbmVyYWxpemF0aW9uIHBlcmZvcm1hbmNlIG9uIG5ldyBkYXRhLiAKCgotIERyb3BvdXQgaXMgdHlwaWNhbGx5IG5vdCB1c2VkIGFmdGVyIGNvbnZvbHV0aW9uYWwgbGF5ZXJzIGluIENOTnMsIGJ1dCByYXRoZXIgYWZ0ZXIgZnVsbHkgY29ubmVjdGVkIGxheWVycy4gSW4gY29udm9sdXRpb25hbCBsYXllcnMsIHRoZSBuZXVyb25zIGFyZSB0eXBpY2FsbHkgc3BhdGlhbGx5IGFycmFuZ2VkIGluIGEgZ3JpZCwgYW5kIGRyb3BwaW5nIG91dCBpbmRpdmlkdWFsIG5ldXJvbnMgYHIgY29sb3JpemUoImNvdWxkIGRpc3J1cHQgdGhlIHNwYXRpYWwgc3RydWN0dXJlIiwicmVkIilgIG9mIHRoZSBmZWF0dXJlIG1hcHMuIEhvd2V2ZXIsIHNvbWUgcmVzZWFyY2hlcnMgaGF2ZSBleHBsb3JlZCBhbHRlcm5hdGl2ZSBmb3JtcyBvZiBzcGF0aWFsIGRyb3BvdXQgdGhhdCBhcmUgZGVzaWduZWQgc3BlY2lmaWNhbGx5IGZvciBjb252b2x1dGlvbmFsIGxheWVycywgc28gaXQncyBhbiBhcmVhIG9mIG9uZ29pbmcgcmVzZWFyY2guCgo6OjogYmx1ZQoKKipOb3RlKio6IEl0J3MgYWxzbyBpbXBvcnRhbnQgdG8gYHIgY29sb3JpemUoImN0dW5lIHRoZSBoeXBlcnBhcmFtZXRlciBvZiBkcm9wb3V0IiwicmVkIilgLCB3aGljaCBpcyB0aGUgZHJvcG91dCBwcm9iYWJpbGl0eSwgdG8gZmluZCB0aGUgb3B0aW1hbCB2YWx1ZSBmb3IgeW91ciBzcGVjaWZpYyBuZXR3b3JrIGFuZCBkYXRhc2V0LiBBZGRpdGlvbmFsbHksIGRyb3BvdXQgY2FuIHNvbWV0aW1lcyBiZSBzZW5zaXRpdmUgdG8gdGhlIG5ldHdvcmsgYXJjaGl0ZWN0dXJlIGFuZCB0aGUgb3JkZXIgaW4gd2hpY2ggbGF5ZXJzIGFyZSBjb25uZWN0ZWQsIHNvIGl0J3Mgd29ydGggZXhwZXJpbWVudGluZyB3aXRoIGRpZmZlcmVudCBhcmNoaXRlY3R1cmVzIGFuZCBsYXllciBjb25uZWN0aW9ucyB0byBzZWUgaWYgcGVyZm9ybWFuY2UgaW1wcm92ZXMuIEZpbmFsbHksIGl0J3MgaW1wb3J0YW50IHRvIG5vdGUgdGhhdCBkcm9wb3V0IGNhbiBzb21ldGltZXMgYmUgb21pdHRlZCBpbiBzbWFsbCBDTk5zIG9yIHNoYWxsb3cgbmV0d29ya3MsIGFzIHRoZSBiZW5lZml0cyBtYXkgbm90IGJlIHNpZ25pZmljYW50IGNvbXBhcmVkIHRvIHRoZSBhZGRlZCBjb21wdXRhdGlvbmFsIG92ZXJoZWFkLgoKOjo6CgojIyMgIEJhdGNoIE5vcm1hbGl6YXRpb24KCi0gU2ltaWxhciB0byBmZWVkLWZvcndhcmQgbmV0d29ya3MsIGJhdGNoIG5vcm1hbGl6YXRpb24gaXMgYSBwb3B1bGFyIHRlY2huaXF1ZSBmb3IgaW1wcm92aW5nIHRoZSBwZXJmb3JtYW5jZSBvZiBDTk5zLiAKCi0gT25lIHNpbXBsZSB0aXAgZm9yIHVzaW5nIGJhdGNoIG5vcm1hbGl6YXRpb24gaXMgdG8gaW5jbHVkZSBpdCBhZnRlciBlYWNoIGNvbnZvbHV0aW9uYWwgb3IgZnVsbHkgY29ubmVjdGVkIGxheWVyIGluIHRoZSBuZXR3b3JrLiBUaGlzIGhlbHBzIHRvIGByIGNvbG9yaXplKCJub3JtYWxpemUgdGhlIG91dHB1dCBvZiBlYWNoIGxheWVyIiwicmVkIilgIGFuZCByZWR1Y2UgdGhlIGludGVybmFsIGNvdmFyaWF0ZSBzaGlmdCwgYW5kIHRoaXMgY2FuIGltcHJvdmUgdGhlIG92ZXJhbGwgc3RhYmlsaXR5IGFuZCBzcGVlZCBvZiB0cmFpbmluZy4gCgoKOjo6IGJsdWUKCioqTm90ZSoqOiBCYXRjaCBub3JtYWxpemF0aW9uIGNhbiBzb21ldGltZXMgYmUgc2Vuc2l0aXZlIHRvIHRoZSBiYXRjaCBzaXplIHVzZWQgZHVyaW5nIHRyYWluaW5nLCBzbyBpdCdzIHdvcnRoIGV4cGVyaW1lbnRpbmcgd2l0aCBkaWZmZXJlbnQgYmF0Y2ggc2l6ZXMgdG8gc2VlIGlmIHBlcmZvcm1hbmNlIGltcHJvdmVzLiBJdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IGJhdGNoIG5vcm1hbGl6YXRpb24gY2FuIHNvbWV0aW1lcyBiZSBvbWl0dGVkIGluIHNtYWxsIENOTnMgb3Igc2hhbGxvdyBuZXR3b3JrcywgYXMgdGhlIGJlbmVmaXRzIG1heSBub3QgYmUgc2lnbmlmaWNhbnQgY29tcGFyZWQgdG8gdGhlIGFkZGVkIGNvbXB1dGF0aW9uYWwgb3ZlcmhlYWQuCgo6OjoKCgojIyMgVW5kZXJzdGFuZGluZyBJbm5lciBMYXllcnMgYW5kIERlcml2ZWQgRmVhdHVyZXMKCi0gIioqVmlzdWFsaXppbmcgYW5kIFVuZGVyc3RhbmRpbmcgQ29udm9sdXRpb25hbCBOZXR3b3JrcyoqIiBpcyBhIHBhcGVyIGJ5IE1hdHRoZXcgWmVpbGVyIGFuZCBSb2IgRmVyZ3VzLCB3aGljaCBwcm9wb3NlcyBhIG1ldGhvZCBmb3IgdmlzdWFsaXppbmcgYW5kIHVuZGVyc3RhbmRpbmcgdGhlIGludGVybmFsIHJlcHJlc2VudGF0aW9ucyBsZWFybmVkIGJ5IENOTnMuIAoKLSBUaGUgYXV0aG9ycyBzdGFydCBieSBoaWdobGlnaHRpbmcgdGhlIGltcG9ydGFuY2Ugb2YgdW5kZXJzdGFuZGluZyBob3cgQ05OcyB3b3JrLiBTcGVjaWZpY2FsbHksIHRoZXkgZm9jdXMgb24gdGhlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGFjdGl2YXRpb24gcGF0dGVybnMgaW4gKip0aGUgZmVhdHVyZSBtYXBzKiogcHJvZHVjZWQgYnkgZWFjaCBsYXllciBvZiB0aGUgbmV0d29yay4gVGhlIGZvbGxvd2luZyBmaWd1cmUgcHJvdmlkZSBzb21lIHVuZGVyc3RhbmRpbmcgb2YgdGhlIGZlYXR1cmVzIGJldHdlZW4gdGhlIGxheWVycy4gRm9yIG1vcmUgZGV0YWlscyByZWZlciB0byB0aGUgb3JpZ2luYWwgcGFwZXIuCgoKCjxjZW50ZXI+CjxmaWd1cmU+PGltZyBzcmM9ImltYWdlcy1jbm4vWmVpbGVyRmVyZ3VzXzIucG5nIiBzdHlsZT0id2lkdGg6NzAwcHgiLz4KPC9maWd1cmU+CjwvY2VudGVyPgoKLSBUaGUgbmV0d29yayBoYXMgbWFueSBjaGFubmVscyBhY3Jvc3MgbXVsdGlwbGUgbGF5ZXJzLCBhbmQgaGVyZSB3ZSBwcmVzZW50IG9ubHkgYSBmZXcgb2YgdGhvc2UgY2hhbm5lbHMsIGZvY3VzaW5nIG9uIGEgcGFpciBvZiBhcmJpdHJhcnkgY2hhbm5lbHMgd2l0aGluIGVhY2ggb2YgdGhlIGxheWVycyAkMiQsICQzJCwgJDQkLCBhbmQgJDUkLiAKCi0gRWFjaCBjaGFubmVsIHRoYXQgd2UgdmlzdWFsaXplIGhhcyBhICQzIFx0aW1lcyAzJCBncmlkIG9mIHN5bnRoZXNpemVkIGltYWdlcyAoY2hhbm5lbCB2aXN1YWxpemF0aW9uKSBhcyB3ZWxsIGFzIGEgbWF0Y2hpbmcgJDMgXHRpbWVzIDMkICBncmlkIG9mIHBhcnRzIG9mIGltYWdlcyBmcm9tIGEgZGF0YXNldCAob3JpZ2luYWwgcmVjZXB0aXZlIGZpZWxkKS4gVGhlc2UgY2hhbm5lbCB2aXN1YWxpemF0aW9ucyBhbmQgb3JpZ2luYWwgcmVjZXB0aXZlIGZpZWxkcyBjYW4gc2VydmUgYXMgYSBgciBjb2xvcml6ZSgidmlzdWFsIGludGVycHJldGF0aW9uIiwicmVkIilgIG9mIHdoYXQgdGhlIHNwZWNpZmljIGNoYW5uZWwgZGV0ZWN0cy4gCgotIEZvciBleGFtcGxlLCB3ZSBzZWUgdGhhdCB0aGUgdHdvIGNoYW5uZWxzIHZpc3VhbGl6ZWQgaW4gbGF5ZXJ+JDIkIGRldGVjdCBgciBjb2xvcml6ZSgic2ltcGxlIGZlYXR1cmVzIiwicmVkIilgIHdpdGggb25lIGNoYW5uZWwgZm9jdXNpbmcgb24gZWRnZXMgYW5kIGFub3RoZXIgY2hhbm5lbCBmb2N1c2luZyBvbiBjaXJjbGVzLiAKCi0gQXMgd2UgYWR2YW5jZSBkZWVwZXIgaW4gdGhlIG5ldHdvcmsgd2Ugc2VlIHRoYXQgdGhlIHR5cGUgb2YgdmlzdWFsIHBhdHRlcm5zIGRldGVjdGVkIGFyZSBtdWNoIG1vcmUgY29tcGxleC4gRm9yIGV4YW1wbGUsIHRoZSB0d28gY2hhbm5lbHMgcHJlc2VudGVkIGZvciBsYXllcn4kNCQgYHIgY29sb3JpemUoImRldGVjdCBwYXJ0cyBvZiBhbmltYWxzIiwicmVkIilgLCBhbmQgdGhlIGNoYW5uZWxzIG9mIGxheWVyfiQ1JCBkZXRlY3Qgc3VjaCByZXByZXNlbnRhdGlvbnMgYXMgd2VsbC4KCi0gTm90ZSBob3dldmVyLCB0aGF0IG9uZSBvZiB0aGUgY2hhbm5lbHMgaW4gbGF5ZXJ+JDUkIHRoYXQgd2UgcHJlc2VudCBhcHBlYXJzIHRvIGRldGVjdCBgciBjb2xvcml6ZSgiZWl0aGVyIGZhY2VzIG9yIGNhciB3aGVlbHMiLCJyZWQiKWAgZXZlbiB0aG91Z2ggdGhlc2UgYXJlIHZlcnkgZGlmZmVyZW50IG9iamVjdHMuIEhlbmNlIGFueSBhdHRlbXB0IHRvIGNhdGVnb3JpemUgY2hhbm5lbHMgYmFzZWQgb24gdGhlaXIgYGBtZWFuaW5nJycgYWxvbmUgaXMgZmFyIGZyb20gYWJzb2x1dGUuIAoKLSBOZXZlcnRoZWxlc3MsIGEgdmlzdWFsIHJlcHJlc2VudGF0aW9uIHN1Y2ggYXMgdGhpcyBGaWd1cmUgY2FuIGhlbHAgdG8gdW5kZXJzdGFuZCB0aGUgZnVuY3Rpb24gb2YgaW5kaXZpZHVhbCBjaGFubmVscyB3aXRoaW4gdGhlIG5ldHdvcmsuCgoKLSBJbiBzdW1tYXJ5LCBgciBjb2xvcml6ZSgiZWFybHkgbGF5ZXJzIG9mIHRoZSBuZXR3b3JrIHRlbmQgdG8gZGV0ZWN0IHNpbXBsZSBmZWF0dXJlcyBzdWNoIGFzIGVkZ2VzIGFuZCB0ZXh0dXJlcyIsInJlZCIpYCwgd2hpbGUgbGF0ZXIgbGF5ZXJzIGRldGVjdCBtb3JlIGNvbXBsZXggZmVhdHVyZXMgc3VjaCBhcyBvYmplY3QgcGFydHMgYW5kIGVudGlyZSBvYmplY3RzLgoKCgojIE90aGVyIExhbmRtYXJrIEFyY2hpdGVjdHVyZXMKCkluIGFkZGl0aW9uIHRvIFZHRzE5IGFyY2hpdGVjdHVyZSB0aGF0IGFjaGlldmVkIGhpZ2ggYWNjdXJhY3kgaW4gdGhlIEltYWdlTmV0IExhcmdlIFNjYWxlIFZpc3VhbCBSZWNvZ25pdGlvbiBDaGFsbGVuZ2UgKElMU1ZSQykgMjAxNCwgdGhlcmUgYXJlIGZldyBvdGhlciBsYW5kbWFyayBhcmNoaXRlY3R1cmVzLiBUaGUgZm9sbG93aW5nIGxpc3QgaXMgc21hbGwgc3VtbWFyeSBvZiB0aGVzZSBtb2RlbHMuCgo6OjogYmx1ZQotICoqUmVzTmV0KiogKFJlc2lkdWFsIE5ldHdvcmspOiBSZXNOZXQgaXMgYSBDTk4gYXJjaGl0ZWN0dXJlIHRoYXQgaW50cm9kdWNlZCByZXNpZHVhbCBibG9ja3MsIHdoaWNoIGFsbG93IGZvciB0aGUgcmV1c2Ugb2YgZmVhdHVyZSBtYXBzIGZyb20gZWFybGllciBsYXllcnMgYnkgYWRkaW5nIHNraXAgY29ubmVjdGlvbnMuIFRoaXMgZW5hYmxlcyB2ZXJ5IGRlZXAgbmV0d29ya3MgKGUuZy4gMTUyIGxheWVycykgdG8gYmUgdHJhaW5lZCBhbmQgYWNoaWV2ZSBzdGF0ZS1vZi10aGUtYXJ0IHBlcmZvcm1hbmNlIG9uIGltYWdlIGNsYXNzaWZpY2F0aW9uIHRhc2tzLgo6OjogCgoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vZmlndXJlNi4xNC1yZXNuZXQucG5nIiAgc3R5bGU9IndpZHRoOjQwMHB4Ii8+IAo8ZmlnY2FwdGlvbj4gQSBzaG9ydGN1dCBjb25uZWN0aW9uIChyZXNpZHVhbCBjb25uZWN0aW9uKSBhcyBwYXJ0IG9mIGEgcmVzaWR1YWwgbmV0d29yay4KPC9maWdjYXB0aW9uPgo8L2ZpZ3VyZT4KPC9jZW50ZXI+CgoKCjo6OiBvcmFuZ2UKLSAqKkluY2VwdGlvbioqIChHb29nTGVOZXQpOiBJbmNlcHRpb24gaXMgYSBDTk4gYXJjaGl0ZWN0dXJlIHRoYXQgaW50cm9kdWNlZCB0aGUgSW5jZXB0aW9uIG1vZHVsZSwgd2hpY2ggdXNlcyBtdWx0aXBsZSBmaWx0ZXIgc2l6ZXMgaW4gcGFyYWxsZWwgdG8gZXh0cmFjdCBmZWF0dXJlcyBhdCBkaWZmZXJlbnQgc2NhbGVzLiBUaGlzIGFsbG93cyBmb3IgZWZmaWNpZW50IHVzZSBvZiBjb21wdXRhdGlvbmFsIHJlc291cmNlcyBhbmQgYWNoaWV2ZXMgaGlnaCBhY2N1cmFjeSBvbiBpbWFnZSBjbGFzc2lmaWNhdGlvbiB0YXNrcy4KOjo6IAoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vZmlndXJlNi4xMy1pbmNlcHRpb24tbW9kdWxlLnBuZyIgIHN0eWxlPSJ3aWR0aDo2MDBweCIvPiAKPGZpZ2NhcHRpb24+IE9uZSBmb3JtIG9mIGFuIGluY2VwdGlvbiBtb2R1bGUsIHBsYXlpbmcgcGFydCBpbiBhbiBpbmNlcHRpb24gbmV0d29yay4gVGhlIGtleSBpZGVhIGlzIHBhcmFsbGVsIGNvbXB1dGF0aW9uIG9mIHZhcmlvdXMgcGF0aHMgZm9sbG93ZWQgYnkgYSBjb25jYXRlbmF0aW5nIG9mIHRoZSBvdXRwdXRzIGZyb20gYWxsIHBhdGhzLgo8L2ZpZ2NhcHRpb24+CjwvZmlndXJlPgo8L2NlbnRlcj4KCjo6OiBvcmFuZ2UKCi0gVGhlIEluY2VwdGlvbiBuZXR3b3JrIGlzIGEgdHlwZSBvZiBjb252b2x1dGlvbmFsIG5ldXJhbCBuZXR3b3JrIGFyY2hpdGVjdHVyZSB0aGF0IHdhcyBpbnRyb2R1Y2VkIGluICoqMjAxNCBieSBHb29nbGUgcmVzZWFyY2hlcnMqKi4gCiAKIC0gVGhlIG1haW4gaWRlYSBiZWhpbmQgdGhlIEluY2VwdGlvbiBuZXR3b3JrIGlzIHRvIHVzZSBgciBjb2xvcml6ZSgibXVsdGlwbGUgZmlsdGVyIHNpemVzIGluIHBhcmFsbGVsIiwicmVkIilgICBhdCBlYWNoIGxheWVyIG9mIHRoZSBuZXR3b3JrLCBpbiBvcmRlciB0byBjYXB0dXJlIGZlYXR1cmVzIG9mIGRpZmZlcmVudCBzY2FsZXMgYW5kIHJlc29sdXRpb25zLgogCiAtIFRoaXMgYXBwcm9hY2ggaXMgZGlmZmVyZW50IGZyb20gb3RoZXIgcG9wdWxhciBDTk4gYXJjaGl0ZWN0dXJlcywgc3VjaCBhcyBWR0csIHdoaWNoIHR5cGljYWxseSB1c2UgYHIgY29sb3JpemUoInNtYWxsICgzeDMpIGZpbHRlcnMiLCJyZWQiKWAgaW4gYWxsIG9mIHRoZWlyIGNvbnZvbHV0aW9uYWwgbGF5ZXJzLgogCiAtIFRoZSBJbmNlcHRpb24gbmV0d29yayBhbHNvIGluY2x1ZGVzIHRoZSB1c2Ugb2Ygc28tY2FsbGVkIGByIGNvbG9yaXplKCJib3R0bGVuZWNrIiwiYmx1ZSIpYCBsYXllcnMsIHdoaWNoIHVzZSBgciBjb2xvcml6ZSgiMXgxIGNvbnZvbHV0aW9ucyIsImJsdWUiKWAgdG8gcmVkdWNlIHRoZSBkaW1lbnNpb25hbGl0eSBvZiB0aGUgaW5wdXQgYmVmb3JlIGFwcGx5aW5nIGxhcmdlciBmaWx0ZXJzLiBUaGlzIGhlbHBzIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgaW4gdGhlIG5ldHdvcmssIHdoaWxlIHN0aWxsIGFsbG93aW5nIGl0IHRvIGNhcHR1cmUgY29tcGxleCBmZWF0dXJlcy4KIAogLSBPbmUgb2YgdGhlIGtleSBpbm5vdmF0aW9ucyBvZiB0aGUgSW5jZXB0aW9uIG5ldHdvcmsgaXMgaXRzIHVzZSBvZiBhbiBgciBjb2xvcml6ZSgiSW5jZXB0aW9uIG1vZHVsZSIsImJsdWUiKWAsIHdoaWNoIGlzIGEgYmFzaWMgYnVpbGRpbmcgYmxvY2sgb2YgdGhlIG5ldHdvcmsgdGhhdCBjb21iaW5lcyB0aGUgdmFyaW91cyBmaWx0ZXIgc2l6ZXMgYW5kIGJvdHRsZW5lY2sgbGF5ZXJzLiBUaGUgSW5jZXB0aW9uIG1vZHVsZSBhbGxvd3MgdGhlIG5ldHdvcmsgdG8gY2FwdHVyZSBjb21wbGV4IGFuZCBtdWx0aS1zY2FsZSBmZWF0dXJlcyBhdCBlYWNoIGxheWVyLCB3aGlsZSBhbHNvIHJlZHVjaW5nIHRoZSBudW1iZXIgb2YgcGFyYW1ldGVycyBpbiB0aGUgbmV0d29yay4KIAo6OjogCgoKOjo6IGJsdWUKLSAqKk1vYmlsZU5ldCoqOiBNb2JpbGVOZXQgaXMgYSBDTk4gYXJjaGl0ZWN0dXJlIGRlc2lnbmVkIGZvciBtb2JpbGUgYW5kIGVtYmVkZGVkIGRldmljZXMgdGhhdCB1c2VzIGRlcHRod2lzZSBzZXBhcmFibGUgY29udm9sdXRpb25zIHRvIHJlZHVjZSB0aGUgbnVtYmVyIG9mIHBhcmFtZXRlcnMgYW5kIGNvbXB1dGF0aW9ucyByZXF1aXJlZCB3aGlsZSBtYWludGFpbmluZyBoaWdoIGFjY3VyYWN5LiBJdCBhY2hpZXZlcyBzdGF0ZS1vZi10aGUtYXJ0IHBlcmZvcm1hbmNlIG9uIG1vYmlsZSBwbGF0Zm9ybXMgYW5kIHJlYWwtdGltZSB2aWRlbyBhbmFseXNpcy4KOjo6IAoKCjo6OiBvcmFuZ2UKLSAqKkRlbnNlTmV0Kio6IERlbnNlTmV0IGlzIGEgQ05OIGFyY2hpdGVjdHVyZSB0aGF0IGludHJvZHVjZXMgZGVuc2UgY29ubmVjdGl2aXR5IGJldHdlZW4gbGF5ZXJzLCB3aGVyZSBlYWNoIGxheWVyIHJlY2VpdmVzIGZlYXR1cmUgbWFwcyBmcm9tIGFsbCBwcmVjZWRpbmcgbGF5ZXJzLiBUaGlzIHByb21vdGVzIGZlYXR1cmUgcmV1c2UgYW5kIHJlZHVjZXMgdGhlIG51bWJlciBvZiBwYXJhbWV0ZXJzIHJlcXVpcmVkLCBsZWFkaW5nIHRvIGhpZ2ggYWNjdXJhY3kgYW5kIGVmZmljaWVudCB0cmFpbmluZy4KOjo6IAoKCjo6OiBibHVlCiAtICoqQWxleE5ldCoqOiBBbGV4TmV0IGlzIGEgQ05OIGFyY2hpdGVjdHVyZSB0aGF0IHdhcyB0aGUgd2lubmVyIG9mIHRoZSAqKklMU1ZSQyAyMDEyKiouIEF0IHRoZSB0aW1lLCBpdCB3YXMgYSBicmVha3Rocm91Z2ggaW4gaW1hZ2UgY2xhc3NpZmljYXRpb24sIGFjaGlldmluZyBzdGF0ZS1vZi10aGUtYXJ0IHBlcmZvcm1hbmNlIG9uIHRoZSBJbWFnZU5ldCBkYXRhc2UuIEl0IGludHJvZHVjZWQgdGhlIGNvbmNlcHQgb2YgdXNpbmcgKipSZUxVKiogYWN0aXZhdGlvbiBmdW5jdGlvbnMsICoqZHJvcG91dCByZWd1bGFyaXphdGlvbioqLCBhbmQgZGF0YSBhdWdtZW50YXRpb24gZm9yIHRyYWluaW5nIGRlZXAgbmV1cmFsIG5ldHdvcmtzLiBJdCBhbHNvIHVzZWQgR1BVIGFjY2VsZXJhdGlvbiB0byBzcGVlZCB1cCB0cmFpbmluZyBhbmQgYWNoaWV2ZWQgYSBzaWduaWZpY2FudCByZWR1Y3Rpb24gaW4gZXJyb3IgcmF0ZSBvbiBpbWFnZSBjbGFzc2lmaWNhdGlvbiB0YXNrcy4KOjo6IAoKCjo6OiBvcmFuZ2UKLSAqKkVmZmljaWVudE5ldCoqIGlzIGEgZmFtaWx5IG9mIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmsgYXJjaGl0ZWN0dXJlcyB0aGF0IHdlcmUgZGV2ZWxvcGVkIHdpdGggdGhlIGFpbSBvZiBwcm92aWRpbmcgYHIgY29sb3JpemUoImJldHRlciBhY2N1cmFjeSBhbmQgZWZmaWNpZW5jeSBpbiB0ZXJtcyBvZiBtb2RlbCBzaXplIGFuZCBjb21wdXRhdGlvbiBjb3N0IiwiYmx1ZSIpYC4gVGhlIGtleSBpZGVhIGlzIHRvIGByIGNvbG9yaXplKCJzeXN0ZW1hdGljYWxseSBzY2FsZSB1cCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgbmV0d29yaydzIHBhcmFtZXRlcnMiLCJibHVlIilgIChzdWNoIGFzIGRlcHRoLCB3aWR0aCwgYW5kIHJlc29sdXRpb24pIGluIGEgYmFsYW5jZWQgd2F5LCB3aGlsZSBhbHNvIGludHJvZHVjaW5nIGEgbmV3IGNvbXBvdW5kIHNjYWxpbmcgbWV0aG9kIHRoYXQgb3B0aW1pemVzIHRoZXNlIGRpbWVuc2lvbnMgYmFzZWQgb24gYSBzZXQgb2YgcHJlLWRlZmluZWQgY29uc3RyYWludHMuIAo6OjogCgoKPGNlbnRlcj4KPGZpZ3VyZT4KPGltZyBzcmM9ImltYWdlcy1jbm4vdGVtcF9lZmZpY2llbnRfbmV0LnBuZyIgIHN0eWxlPSJ3aWR0aDo2MDBweCIvPiAKPGZpZ2NhcHRpb24+IFBlcmZvcm1hbmNlIG9mIHZhcmlvdXMgY29udm9sdXRpb25hbCBtb2RlbHMgYXMgd2VsbCBhcyBlZmZpY2llbnQgbmV0CjwvZmlnY2FwdGlvbj4KPC9maWd1cmU+CjwvY2VudGVyPgoKCgoKIyBCZXlvbmQgQ2xhc3NpZmljYXRpb24KCi0gZmFyIHdlIGhhdmUgZm9jdXNlZCBvbiB0aGUgaW50ZXJuYWxzIG9mIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzIHdpdGggYSBwYXJ0aWN1bGFyIGVtcGhhc2lzIG9uIHRoZSB0YXNrIG9mICoqaW1hZ2UgY2xhc3NpZmljYXRpb24qKiwgZS5nLiBkZXRlcm1pbmluZyBpZiBhbiBpbWFnZSBpcyB0aGF0IG9mIGEgY2F0IG9yIGEgZG9nLiAKCi0gSG93ZXZlciwgdGhlcmUgYXJlIHNldmVyYWwgb3RoZXIgaW1wb3J0YW50IGltYWdlIGFuYWx5c2lzIHRhc2tzIHRoYXQgYXJlIGFsc28gaGFuZGxlZCB3aXRoIGNvbnZvbHV0aW9uYWwgbmV1cmFsIG5ldHdvcmtzLiBUaGVzZSB0YXNrcyBkZWFsIHdpdGggYW5hbHlzaXMgYW5kIHVuZGVyc3RhbmRpbmcgb2YgYW4gaW1hZ2UgaW5jbHVkaW5nIGByIGNvbG9yaXplKCJ0aGUgbG9jYXRpb24gb2Ygb2JqZWN0cywgdGhlIGNvdW50IG9mIG9iamVjdHMsIHNlcGFyYXRpbmcgYmV0d2VlbiBkaWZmZXJlbnQgc2VtYW50aWMgZmVhdHVyZXMgb2YgdGhlIGltYWdlLCIsImJsdWUiKWAgYW5kIG1vcmUuIAoKOjo6IGJsdWUKCioqTm90ZSoqOiBJbiB0ZXJtcyBvZiB0aGUgaW5wdXQgZGF0YSwgaXQgaXMgaW1wb3J0YW50IHRvIGtlZXAgaW4gbWluZCB0aGF0IG5vdCBhbGwgZGF0YSBpcyBtYWRlIG9mIG1vbm9jaHJvbWUgb3IgY29sb3IgaW1hZ2VzLiBXaXRoaW4gY29tcHV0ZXIgdmlzaW9uLCBvbmUgb2Z0ZW4gZGVhbHMgd2l0aCAqKmltYWdlIHNlcXVlbmNlcyoqIChzaG9ydCBtb3ZpZXMpLCBvciBpbWFnZXMgdGhhdCBoYXZlIG1vcmUgdGhhbiAzIGNoYW5uZWxzLiBGb3IgZXhhbXBsZSwgc29tZSBpbWFnZXMgbWF5IGFsc28gaGF2ZSBhICoqZGlzdGFuY2UgY2hhbm5lbCoqIGNhcHR1cmluZyB0aGUgZGlzdGFuY2UgZnJvbSB0aGUgY2FtZXJhIHBlciBwaXhlbC4gRnVydGhlciwgbm9uLWltYWdlIGRhdGEgY2FuIGFsc28gYmUgaGFuZGxlZCB2aWEgY29udm9sdXRpb25hbCBuZXR3b3Jrcy4gT25lIHN1Y2ggZXhhbXBsZSBpcyAqKmZNUkkgKGZ1bmN0aW9uYWwgbWFnbmV0aWMgcmVzb25hbmNlIGltYWdpbmcpKiogZGF0YSB3aGljaCBpcyA0IGRpbWVuc2lvbmFsIGluIG5hdHVyZSBhcyBpdCByZWNvcmRzIHRoZSBzdGF0ZSBvZiBwaHlzaWNhbCBsb2NhdGlvbnMgKGUuZy4sIGJsb29kLW94eWdlbi1sZXZlbC1kZXBlbmRlbnQgKEJPTEQpIHNpZ25hbCkgaW4gdGhyZWUgZGltZW5zaW9ucyBvdmVyIHRpbWUuIAoKOjo6CgojIyMgQ29udm9sdXRpb25hbCBOZXR3b3JrcyBhbmQgS2V5IENvbXB1dGVyIFZpc2lvbiBUYXNrcwoKCgo6OjpvcmFuZ2UKLSBBcyBtZW50aW9uZWQgYWJvdmUsIGNsYXNzaWZpY2F0aW9uIHNlcnZlcyBhcyBhIHNpbXBsZSBhbmQgdXNlZnVsIGV4YW1wbGUuIEZvciBhbiBpbnB1dCBpbWFnZSAkeCQsIGEgY29udm9sdXRpb25hbCBuZXVyYWwgbmV0d29yayAkZl9cdGhldGEoXGNkb3QpJCwgaGFzIG91dHB1dCAkXGhhdHt5fSA9IGZfXHRoZXRhKHgpJCB3aGljaCBpcyBhICoqdmVjdG9yIG9mIHByb2JhYmlsaXRpZXMqKiB3aGVyZSB0aGUgYHIgY29sb3JpemUoImhpZ2hlc3QgcHJvYmFiaWxpdHkiLCJibHVlIilgIHR5cGljYWxseSBkZXRlcm1pbmVzIHRoZSBhcHByb3ByaWF0ZSBsYWJlbCBmb3IgdGhlIGltYWdlLiAKCgotIEFzIHdhcyBldmlkZW50IGZyb20gb3VyIGRldGFpbGVkIHN0dWR5IG9mIHRoZSBWR0cxOSBtb2RlbCAgYW5kIG90aGVyIGFyY2hpdGVjdHVyZXMsICoqaW5pdGlhbCBsYXllcnMqKiBvZiB0aGUgbW9kZWwgJGZfXHRoZXRhKFxjZG90KSQgYXJlIHR5cGljYWxseSAqKmNvbnZvbHV0aW9uYWwqKiwgYW5kIHRoZSBgciBjb2xvcml6ZSgiZmluYWwgbGF5ZXJzIiwiYmx1ZSIpYCBhcmUgdHlwaWNhbGx5IGByIGNvbG9yaXplKCJmdWxseSBjb25uZWN0ZWQgbGF5ZXJzIiwiYmx1ZSIpYC4KCi0gVGhlc2UgZmluYWwgbGF5ZXJzICoqaGVscCoqIHRyYW5zZm9ybSB0aGUgaW50ZXJuYWwgZGVyaXZlZCBmZWF0dXJlcyBpbiB0aGUgbmV0d29yayBpbnRvIHRoZSBvdXRwdXQgdmVjdG9yIG9mIHByb2JhYmlsaXRpZXMgJFxoYXR7eX0kLgoKLSBXaGVuIG9uZSBjb25zaWRlcnMgdGFza3Mgb3RoZXIgdGhhbiBjbGFzc2lmaWNhdGlvbiwgaXQgaXMgb2Z0ZW4gY29tbW9uIHRvICoqcmVwbGFjZSoqIHRoZSBmaW5hbCBsYXllcnMgb2YgdGhlIG5ldHdvcmsgd2l0aCBvdGhlciBsYXllcnMgc3VjaCB0aGF0IHRoZSBvdXRwdXQgJFxoYXR7eX0kIHN1aXRlcyAqKnRoZSBkZXNpcmVkIHRhc2sqKi4KCjo6OiAKCi0gVGhlIGZvbGxvd2luZyBmaWd1cmUgaWxsdXN0cmF0ZXMga2V5IGNvbXB1dGVyIHZpc2lvbiB0YXNrcyBmb3IgaW1hZ2VzLiAKCgo8Y2VudGVyPgo8ZmlndXJlPgo8aW1nIHNyYz0iaW1hZ2VzLWNubi9GSUdVUkU2LTE2LnBuZyIgIHN0eWxlPSJ3aWR0aDo2MDBweCIvPiAKPC9maWd1cmU+CjwvY2VudGVyPgoKXAoKCi0gKipPYmplY3QgbG9jYWxpemF0aW9uKiogd2hpY2ggaXMgdGhlIHRhc2sgb2YgaWRlbnRpZnlpbmcgdGhlIGxvY2F0aW9uIG9mIGFuIG9iamVjdCBpbiBhbiBpbWFnZSwgYXMgd2VsbCBhcyBwb3NzaWJseSB0aGUgdHlwZSBvZiBvYmplY3QgaW4gd2hpY2ggY2FzZSB0aGUgdGFzayBpcyBjYWxsZWQgKipsb2NhbGl6YXRpb24gYW5kIGNsYXNzaWZpY2F0aW9uKiouIAoKLSAqKk9iamVjdCBkZXRlY3Rpb24qKiB3aGljaCBpcyB0aGUgdGFzayBvZiBkZXRlY3RpbmcgbXVsdGlwbGUgaW5zdGFuY2VzIG9mIGFuIG9iamVjdCBpbiBhbiBpbWFnZSwgYWxzbyBzZXBhcmF0aW5nIGJldHdlZW4gdGhlIG9iamVjdHMgYW5kIGNsYXNzaWZ5aW5nIHRoZWlyIHR5cGUuCgotICoqTGFuZG1hcmsgZGV0ZWN0aW9uKiosIHdoaWNoIGlzIHRoZSB0YXNrIG9mIGlkZW50aWZ5aW5nIHRoZSBzcGVjaWZpYyBwaXhlbCBsb2NhdGlvbnMgb2YgbGFuZG1hcmtzIGluIGFuIGltYWdlLiAKCi0gKipTZW1hbnRpYyBzZWdtZW50YXRpb24qKiwgd2hpY2ggaXMgdGhlIHByb2Nlc3Mgb2YgY2xhc3NpZnlpbmcgZWFjaCBpbmRpdmlkdWFsIHBpeGVsIHRvIGJlIG9mIGEgZGlmZmVyZW50IGNsYXNzIGZyb20gYSBmaW5pdGUgc2V0IG9mIGNsYXNzZXMgKHBpeGVsIHdpc2UgY2xhc3NpZmljYXRpb24pLgoKLSAqKkluc3RhbmNlIHNlZ21lbnRhdGlvbioqLCB3aGljaCBmaW5kcyBkaWZmZXJlbnQgaW5zdGFuY2VzIG9mIG9iamVjdHMgaW4gdGhlIGltYWdlIGFuZCBzZXBhcmF0ZXMgcGl4ZWxzIHRvIGJlIG9mIGRpZmZlcmVudCBpbnN0YW5jZXMuIAoKLSAqKklkZW50aWZpY2F0aW9uKiogKGZhY2UgcmVjb2duaXRpb24pLCB3aGljaCBkZXRlcm1pbmVzIGlmIGFuIGltYWdlIGlzIHRoYXQgb2YgYSBzcGVjaWZpYyBpbnN0YW5jZSAob3IgcGVyc29uKS4KCgpcCgoKKipMZXQgdXMgbm93IGNvbnNpZGVyIHBvc3NpYmxlIGZvcm1zIG9mIHRoZSBvdXRwdXQgJFx3aWRlaGF0e3l9JCoqIAoKLSBgciBjb2xvcml6ZSgiRm9yIG9iamVjdCBsb2NhbGl6YXRpb24iLCJibHVlIilgLCAkXHdpZGVoYXR7eX0kIG5lZWRzIHRvIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgYSAqKmJvdW5kaW5nIGJveCoqIHdoaWNoIGxvY2F0ZXMgdGhlIG9iamVjdC4gVGhpcyBjYW4gYmUgaW4gdGhlIGZvcm0gb2YgJChcd2lkZWhhdHt5fV94LFx3aWRlaGF0e3l9X3ksXHdpZGVoYXR7eX1faCxcd2lkZWhhdHt5fV93KSQgd2hlcmUgJFx3aWRlaGF0e3l9X3gkIGFuZCAkXHdpZGVoYXR7eX1feSQgYXJlIHRoZSBjb29yZGluYXRlcyBvZiAoc2F5KSB0aGUgdXBwZXIgbGVmdCBjb3JuZXIgb2YgdGhlIGJvdW5kaW5nIGJveCBhbmQgJFx3aWRlaGF0e3l9X2gkLCAkXHdpZGVoYXR7eX1fdyQgYXJlIHRoZSBoZWlnaHQgYW5kIHdpZHRoIG9mIHRoZSBib3VuZGluZyBib3gsIHJlc3BlY3RpdmVseS4gCgotIGByIGNvbG9yaXplKCJGb3Igb2JqZWN0IGRldGVjdGlvbiIsInJlZCIpYCwgYSBjb2xsZWN0aW9uIG9mIG11bHRpcGxlIGJvdW5kaW5nIGJveGVzIG5lZWRzIHRvIGJlIHN1cHBsaWVkLiAKCi0gYHIgY29sb3JpemUoIkZvciBsYW5kbWFyayBkZXRlY3Rpb24iLCJibHVlIilgLCBhIGxpc3Qgb2YgY29vcmRpbmF0ZXMgb2YgdGhlIGxvY2F0aW9ucyBvZiBsYW5kbWFya3MgY29tcHJpc2VzIHRoZSBvdXRwdXQuIAoKLSBgciBjb2xvcml6ZSgiRm9yIHNlbWFudGljIHNlZ21lbnRhdGlvbiIsInJlZCIpYCwgZWFjaCBwaXhlbCBsb2NhdGlvbiBpbiB0aGUgaW5wdXQgaW1hZ2UsICR4JCwgaGFzIGFuIGFzc29jaWF0ZWQgcHJvYmFiaWxpdHkgdmVjdG9yIG9mIGNsYXNzZXMgaW4gdGhlIG91dHB1dCAkXHdpZGVoYXR7eX0kLiBIZW5jZSBpbiB0aGlzIGNhc2UsICRcd2lkZWhhdHt5fSQgY2FuIGJlIHJlcHJlc2VudGVkIGFzIGEgdGVuc29yIHdpdGggd2lkdGggYW5kIGhlaWdodCBkaW1lbnNpb25zIHRoZSBzYW1lIGFzIHRoZSBpbnB1dCBpbWFnZSwgYW5kIGEgZGVwdGggZGltZW5zaW9uIHdoaWNoIGlzIHRoZSBudW1iZXIgb2YgY2xhc3NlcyBpbiB0aGUgc2VnbWVudGF0aW9uLiAKCi0gYHIgY29sb3JpemUoIkZvciBpbnN0YW5jZSBzZWdtZW50YXRpb24iLCJibHVlIilgLCB0aGUgb3V0cHV0IGlzIHNpbWlsYXIgdG8gdGhhdCBvZiBzZW1hbnRpYyBzZWdtZW50YXRpb24sIGJ1dCBpbnN0ZWFkIG9mIHJlY29yZGluZyBwcm9iYWJpbGl0aWVzIG9mIGNsYXNzZXMsIHRoZSBkZXB0aCBkaW1lbnNpb24gb2YgdGhlIG91dHB1dCAkXHdpZGVoYXR7eX0kIGlzIHVzZWQgZm9yIGRldGVybWluaW5nIHRoZSBzcGVjaWZpYyBpbnN0YW5jZSBvZiBhbnkgZ2l2ZW4gcGl4ZWwuIAoKLSBGaW5hbGx5LCBpbiB0aGUgY2FzZSBvZiBgciBjb2xvcml6ZSgiaWRlbnRpZmljYXRpb24sIG9yIGZhY2UgcmVjb2duaXRpb24iLCJyZWQiKWAsIHRoZSBvdXRwdXQgaXMgb2Z0ZW4ganVzdCBhIHByb2JhYmlsaXR5IGFzIGluIGEgYmluYXJ5IGNsYXNzaWZpZXIsIHNpbmNlIHRoZSB0YXNrIGlzIHRvIGRldGVybWluZSBpZiBhIGZhY2UgaW1hZ2UgbWF0Y2hlcyBhIGdpdmVuIHByZS1zdG9yZWQgdGVtcGxhdGUgb3Igbm90LiBUaGUgc3BlY2lhbGl0eSBpbiB0aGlzIGNhc2UgaXMgdGhhdCB0aGUgKippbnB1dCoqICR4JCBpcyB0eXBpY2FsbHkgY29tcG9zZWQgb2YgKip0d28gaW1hZ2VzKiosIHdoZXJlIG9uZSBpcyB0aGUgdGVtcGxhdGUgb2YgdGhlIHBlcnNvbiAoZS5nLiBhIHN0b3JlZCBpbWFnZSBpbiBhIHNlY3VyaXR5IGRhdGFiYXNlKSwgYW5kIHRoZSBvdGhlciBpbWFnZSBpcyB0aGUgKipjdXJyZW50IGltYWdlIHRha2VuKiouCgotLS0KCiMjIyBPYmplY3QgTG9jYWxpemF0aW9uIAoKOjo6b3JhbmdlCi0gVG8gZ2V0IGEgZmVlbCBmb3Igb2JqZWN0IGxvY2FsaXphdGlvbiBhc3N1bWUgdGhhdCB3ZSB3aXNoIHRvIHRyYWluIGEgQ05OIHRoYXQgb3BlcmF0ZXMgb24gYW4gaW5wdXQgaW1hZ2UgJHgkIGFuZCBkZXRlcm1pbmVzIGlmIHRoZSBpbWFnZSBjb250YWlucyBhICRcdGV4dHR0e2JpcmR9JCBvciBhICRcdGV4dHR0e3BsYW5lfSQgKGNsYXNzaWZpY2F0aW9uKS4gCgotIFRoZSBtb2RlbCdzIHNlY29uZCBnb2FsIGlzIHRvIGRldGVybWluZSB0aGUgKipzcGVjaWZpYyBsb2NhdGlvbioqICQoXHdpZGVoYXR7eX1feCxcd2lkZWhhdHt5fV95LFx3aWRlaGF0e3l9X2gsXHdpZGVoYXR7eX1fdykkIG9mIHRoYXQgb2JqZWN0IChsb2NhbGl6YXRpb24pLiBJbWFnZXMgd2l0aCBtdWx0aXBsZSBiaXJkcyBvciBwbGFuZXMgYXJlIG5vdCBjb25zaWRlcmVkLiBJbWFnZXMgd2l0aG91dCBhIGJpcmQgYW5kIHdpdGhvdXQgYSBwbGFuZSBhcmUgcG9zc2libGUgYW5kIGluIHRoaXMgY2FzZSB0aGUgb3V0cHV0IHlpZWxkcyAkXHRleHR0dHtub3RoaW5nfSQuIAo6OjoKCgpPbmUgd2F5IHRvIGVuY29kZSB0aGUgb3V0cHV0IGlzCiQkClx3aWRlaGF0e3l9PShcd2lkZWhhdHtwfV97XHRleHR0dHtub3RoaW5nfX0sIH5cd2lkZWhhdHtwfV97XHRleHR0dHtiaXJkfX0sIH5cd2lkZWhhdHtwfV97XHRleHR0dHtwbGFuZX19LCBcd2lkZWhhdHt5fV94LFx3aWRlaGF0e3l9X3ksXHdpZGVoYXR7eX1faCxcd2lkZWhhdHt5fV93KSwKJCQKCndoZXJlIGFzIGluIHN0YW5kYXJkIGNsYXNzaWZpY2F0aW9uIGV4YW1wbGVzICQoXHdpZGVoYXR7cH1fe1x0ZXh0dHR7bm90aGluZ319LCB+XHdpZGVoYXR7cH1fe1x0ZXh0dHR7YmlyZH19LCB+XHdpZGVoYXR7cH1fe1x0ZXh0dHR7cGxhbmV9fSkkIGlzIGEgcHJvYmFiaWxpdHkgdmVjdG9yLCBhbmQgdGhlIG90aGVyIGNvb3JkaW5hdGVzIGRlZmluZSBhIGJvdW5kaW5nIGJveC4KCgotIEhlcmUgYW4gb3V0cHV0IHRoYXQgaGFzICRcd2lkZWhhdHtwfV97XHRleHR0dHtub3RoaW5nfX0kIGdyZWF0ZXIgdGhhbiBlYWNoIG9mICRcd2lkZWhhdHtwfV97XHRleHR0dHtiaXJkfX0kIGFuZCAkXHdpZGVoYXR7cH1fe1x0ZXh0dHR7cGxhbmV9fSQgaW1wbGllcyBhIHByZWRpY3Rpb24gb2Ygbm8gYmlyZCBhbmQgbm8gcGxhbmUuIAoKLSBJbiB0ZXJtcyBvZiB0cmFpbmluZyBkYXRhLCBmb3IgZWFjaCBpbnB1dCBpbWFnZSB3ZSBkZW5vdGUgdGhlIG91dHB1dCBhcyAkeSQgd2hlcmUgaW1hZ2VzIHdpdGhvdXQgYSBiaXJkIG9yIGEgcGxhbmUgYXJlIGxhYmVsZWQgYXMsICR5ID0gKDEsIDAsIDAsIFxlbXB0eXNldCxcZW1wdHlzZXQsXGVtcHR5c2V0LFxlbXB0eXNldCkkLCB3aGVyZSAkXGVtcHR5c2V0JCBhcmUgYGBkbyBub3QgY2FyZScnIHZhbHVlcy4gSW1hZ2VzIHdpdGggYSBiaXJkIGFyZSBsYWJlbGVkIGFzICR5ID0gKDAsMSwwLCB7eX1feCwge3l9X3ksIHt5fV9oLHt5fV93KSQgd2hlcmUgdGhlIGJvdW5kaW5nIGJveCAkKHt5fV94LCB7eX1feSwge3l9X2gse3l9X3cpJCBpcyB0eXBpY2FsbHkgYmFzZWQgb24gYSBtYW51YWwgZGV0ZXJtaW5hdGlvbiBieSBhIGh1bWFuIGFubm90YXRvci4gU2ltaWxhcmx5LCBpbWFnZXMgd2l0aCBhIHBsYW5lIGFyZSBsYWJlbGVkIGFzICR5ID0gKDAsMCwxLCB7eX1feCwge3l9X3ksIHt5fV9oLHt5fV93KSQuCgotIFdlIG5vdyBjb25zdHJ1Y3QgYSBgciBjb2xvcml6ZSgibG9zcyBmdW5jdGlvbiIsInJlZCIpYCB0aGF0IGNhcHR1cmVzIGNsb3NlbmVzcyBvZiAkXHdpZGVoYXR7eX0kIGFuZCAkeSQuIEZvciB0aGlzIHdlIGZpcnN0IHNlcGFyYXRlIHRoZSBjbGFzc2lmaWNhdGlvbiBhbmQgbG9jYWxpemF0aW9uIG9iamVjdGl2ZXMgaW50byBhIGxvc3MgJENfe1x0ZXh0e2NsYXNzaWZpY2F0aW9ufX0oXHRoZXRhIFwsO1wsIFx3aWRlaGF0e3l9LCB5KSQgYW5kICRDX3tcdGV4dHtsb2NhbGl6YXRpb259fShcdGhldGEgXCw7XCwgXHdpZGVoYXR7eX0sIHkpJC4gVGhlIGZvcm1lciBkZXBlbmRzIG9ubHkgb24gdGhlIHByb2JhYmlsaXR5IGNvbXBvbmVudHMgaW4gJFx3aWRlaGF0e3l9JCBhbmQgJHkkLCBhbmQgdGhlIGxhdHRlciBkZXBlbmRzIG9ubHkgb24gdGhlIGJvdW5kaW5nIGJveCBjb21wb25lbnRzIGluICRcd2lkZWhhdHt5fSQgYW5kICR5JC4gRm9yIHRoZSBgciBjb2xvcml6ZSgiY2xhc3NpZmljYXRpb24gbG9zcyIsInJlZCIpYCwgd2UgdXNlIGByIGNvbG9yaXplKCJjYXRlZ29yaWNhbCBjcm9zcyBlbnRyb3B5IiwicmVkIilgLiBGb3IgdGhlIGByIGNvbG9yaXplKCJsb2NhbGl6YXRpb24gbG9zcyIsImJsdWUiKWAsIHdlIHVzZSBhIGByIGNvbG9yaXplKCJtZWFuIHNxdWFyZWQgZXJyb3IiLCJibHVlIilgLCBhcHBsaWVkIHRvIHRoZSBmb3VyIGJvdW5kaW5nIGJveCBjb21wb25lbnRzLgoKOjo6Ymx1ZQpUaGUgdHdvIHNlcGFyYXRlIGxvc3NlcyBhcmUgdGhlbiBjb21iaW5lZCBzdWNoIHRoYXQgdGhlIGxvc3MgZm9yIGEgc3BlY2lmaWMgb2JzZXJ2YXRpb24gaXMsCgpcWwpDX3tcdGV4dHtjbGFzc2lmaWNhdGlvbn19KFx0aGV0YSBcLDtcLCBcd2lkZWhhdHt5fSwgeSkgfit+IFxnYW1tYSAgXGNkb3QgICgxLXlfMSkgXGNkb3QgIENfe1x0ZXh0e2xvY2FsaXphdGlvbn19KFx0aGV0YSBcLDtcLCBcd2lkZWhhdHt5fSwgeSksClxdCgp3aGVyZSAkXGdhbW1hID4gMCQgaXMgYSBoeXBlci1wYXJhbWV0ZXIgdXNlZCB0byB3ZWlnaCB0aGUgdHdvIGxvc3NlcyBhbmQgdGFrZW4gYXMgJFxnYW1tYT0xJCBieSBkZWZhdWx0LiBPYnNlcnZlIHRoYXQgJHlfMSA9IDEkIHdoZW4gdGhlIGxhYmVsIGlzICRcdGV4dHR0e25vdGhpbmd9JCBhbmQgaXMgb3RoZXJ3aXNlICQwJCBhbmQgdGh1cyBmb3IgbGFiZWxzIGluIHRoZSB0cmFpbmluZyBkYXRhIHdpdGhvdXQgYSBiaXJkIG9yIGEgcGxhbmUgb25seSB0aGUgY2xhc3NpZmljYXRpb24gb2JqZWN0aXZlIGlzIHVzZWQuCjo6OgoKLSBUbyBwZXJmb3JtIG9iamVjdCBsb2NhbGl6YXRpb24sIHNheSB3aXRoIGEgbW9kZWwgbGlrZSAqKlZHRzE5KiosIHRoZSBuZXR3b3JrIGNhbiBiZSBtb2RpZmllZCBieSBhZGRpbmcgYWRkaXRpb25hbCBsYXllcnMgYXQgdGhlIGVuZCBvZiB0aGUgYXJjaGl0ZWN0dXJlIHRvIHByZWRpY3QgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBib3VuZGluZyBib3guIFRoaXMgY2FuIGJlIGFjaGlldmVkIGJ5IGByIGNvbG9yaXplKCJhdHRhY2hpbmcgYSByZWdyZXNzaW9uIGhlYWQgdG8gdGhlIG91dHB1dCBvZiB0aGUgZmluYWwgY29udm9sdXRpb25hbCBsYXllciBvZiB0aGUgbmV0d29yayIsImJsdWUiKWAuIFRoZSByZWdyZXNzaW9uIGhlYWQgY29uc2lzdHMgb2YgZnVsbHkgY29ubmVjdGVkIGxheWVycyB0aGF0IHByZWRpY3QgdGhlIGNvb3JkaW5hdGVzIG9mIHRoZSBib3VuZGluZyBib3guIFN1Y2ggc2ltcGxlIG1vZGlmaWNhdGlvbnMgb2YgbmV0d29ya3MgdGhhdCB3ZXJlIG90aGVyd2lzZSBkZXNpZ25lZCBmb3IgY2xhc3NpZmljYXRpb24gYXJlIGFsd2F5cyBwb3NzaWJsZS4KCgojIyMgUHJhY3RpY2UgRXhjZXJjaXNlCgo6Ojogb3JhbmdlCioqUHJhY3RpY2FsIDM6KioKCi0gICBPcGVuIFR1dG9yaWFsIDMgb2YgQ05OIGF2YWlsYWJsZSBvbiB0aGUgd29ya3Nob3AgR2l0SHViIHBhZ2UuIEFsdGVybmF0aXZlbHksIGNsaWNrIDxhIGhyZWY9Imh0dHBzOi8vY29sYWIucmVzZWFyY2guZ29vZ2xlLmNvbS9kcml2ZS8xd1pfTEpUbW9DZko0Q2E3QjM4SGQ3VG53dWl0Qk1BS1Yjc2Nyb2xsVG89Qy01bkFyR2tNZTBnInRhcmdldD0iX2JsYW5rIj5oZXJlPC9hPi4KCi0gICBTYXZlIGEgY29weSBvZiB0aGlzIGluIHlvdXIgR29vZ2xlIENvbGFiLgoKLSAgIEluIHRoaXMgZXhlcmNpc2UsIHdlIGJ1aWxkIGEgc2ltcGxlIENOTiBmb3IgY2xhc3NpZmljYXRpb24gdXNpbmcgTU5JU1QgZGF0YXNldC4KOjo6